Sidebar Mechanics of a Custom Bootstrap Site

Sidebar Mechanics of a Custom Bootstrap Site

Ghost development makes it easy to define a custom sidebar by using partials. However, the partial runs in the context of the post.hbs or page.hbs files. This limits the functionality that you can provide.

The sidebar partial hierarchy

For my custom Bootstrap theme, I created two separate partial files for the sidebar; one for the author page, and one for the page, post, and tag templates. I did this so that I could display information about myself on those pages. This, of course, assumes that you are running a single author blog. It would be a bit more complicated for multiple authors (if you even wanted to do such a thing at all).

Here is the code for the post page page which shows the location of the sidebar as a bootstrap <div /> with the id of "sidebar-container" and a css class of col-md-4.

<div class="row">
	<div id="sidebar-container" class="col-md-4">
		{{> sidebar author}}
	</div> <!-- .col-md-4 -->
	<div class="col-md-8">
		<!-- content... -->
	</div> <!-- .col-md-8 -->

Notice that I sent in the author context so that the sidebar would have access to the author data.

Here is the author.hbs partial that is used in the above code:

<!-- sidebar -->
{{!include the various components}}
{{> "search-form"}}
<div id="sidebar-component-container"></div>
{{> "bio-panel"}}
{{> "sidebar-theme-picker"}}
<!-- /sidebar -->

This sidebar adds a sidebar component container (which I'll discuss below) as well as calls to bio-panel and sidebar-theme-picker partials.

Here's the bio-panel.hbs which takes advantage of the fact that we passed in the author context from the page:

<a name="author" class="sr-only"></a>
<div class="panel panel-primary">
	<div class="panel-heading">
		About {{name}}
	<div class="panel-body">
		{{#if image}}
		<img class="img-thumbnail center-block" src="{{image}}" alt="{{name}}'s Picture" />
		<h1 class="author-title center-block">{{name}}</h1>
		{{#if bio}}
			<div class="bio-container">
				<h2 class="author-bio text-justify">{{bio}}</h2>
		<div class="author-meta center-block sidebar-meta">
			{{#if location}}<p><i class="fa fa-map-marker"></i> <small>{{location}} <a href="{{ encode location }}" aria-label="{{location}} map link" target="locationMap">(map)</a></small></p>{{/if}}
			{{#if website}}<p><i class="fa fa-link"></i> <small><a href="{{website}}">{{website}}</a></small></p>{{/if}}

The <a /> with the sr-only class is simply a bookmark for screen readers. It is not displayed to the user.

All of the data that is displayed is coming from the author context that we passed into the original partial.

This works well when we only need to use the information from a single context like page or post where we have author information, but what happens when we want to display the author information from a context that has multiple possible authors? In this case, we pass in the author context like so (this is from the tag.hbs page):

<div id="sidebar-container" class="col-md-4">
	{{> "sidebar"}}
</div> <!-- .col-md-4 -->

In this case, I am simply pulling the author context off of the first post. Obviously, if the author you want to display in the sidebar is not the author of the first post, you'll need to pick a different post.

Note: there are (as of this writing) experimental API calls you can make to retrieve some of this information, but it outputs some code in the html header that I am not comfortable with. I just get a weird feeling doing it that way.

Sleight-of-hand tricks for sidebar content out of context

If you are reading this post and the Bootstrap theme is enabled, you should see a panel with Social Media share buttons in it. As you can see from the code above, those links are not included in any of the sidebar code.

For the post social media links to work, I have included a hidden <div /> in the post.hbs template that contains another <div /> decorated with the css class of sidebar-component.

<div class="panel panel-primary share sidebar-component">
	<div class="panel-heading">
		<p class="panel-title">Share this post</p>
	</div> <!-- .panel-heading (share) -->
	<div class="panel-body">
		<a class="text-primary share-link" href="{{encode title}}&amp;url={{url absolute="true"}}"
		   onclick=", 'twitter-share', 'width=550,height=235');return false;"
		   title="Share on Twitter" aria-label="Share on Twitter"><i class="fa fa-3x fa-twitter-square"></i><span class="sr-only">Twitter</span></a>
		<a class="text-primary share-link" href="{{url absolute="true"}}"
		   onclick=", 'facebook-share','width=580,height=296');return false;"
		   title="Share on Facebook" aria-label="Share on Facebook"><i class="fa fa-3x fa-facebook-square"></i><span class="sr-only">Facebook</span></a>
		<a class="text-primary share-link" href="{{url absolute="true"}}"
		   onclick=", 'google-plus-share', 'width=490,height=530');return false;"
		   title="Share on Google+" aria-label="Share on Google+"><i class="fa fa-3x fa-google-plus-square"></i><span class="sr-only">Google+</span></a>
	</div> <!-- .panel-body (share) -->
</div> <!-- .panel (share) -->

Then, there is code in the page initialization that moves any of those divs to the <div /> in the sidebar with the id of sidebar-component-container.

Here is that code:

function moveSidebarItems() {
	// move items to the sidebar
	$('.sidebar-component').each(function () {


This is pretty self-explanatory. It uses jQuery to move the intended sidebar content to the sidebar after the page has loaded. That means that the server has already processed it and used the context that the div is defined under. In simple terms, it means that you can hide a div in any part of a page or in any partial under any context and display data about that context, then simply make sure that the div has the class sidebar-component attached to it and it will get moved into place automatically. It should be noted that there is no defined order as that is out of the scope of this discussion. It could be done with custom html attributes (data-sidebar-order or similar attribute and ordered during the move at page ready).