Adobe Robohelp

Get new posts delivered straight to your inbox.

Subscriber count: 3,725

Stitcher radio

follow us in feedly

Want more tech comm blogs to follow? See my Tech Comm Collection of Blogs on Feedly.

Adobe FrameMaker

Building a table of contents with DITA versus Jekyll

Apr 2, 2015 • dita, jekyll

In my ongoing series comparing Jekyll against DITA, I want to touch on how you construct a table of contents.

Creating a TOC with DITA

The ditamap file in DITA is arguably the most important file in a DITA project, and it has a lot of features. Basically, the ditamap defines the table of contents for the project, which is how users navigate all the files (apart from search).

Additionally, any file that you want included in your output must appear in your ditamap file. Otherwise it's excluded from the build.

A project can have multiple ditamap files. You might have separate ditamap file for each output, or you could use the same ditamap file with conditional tags for multiple outputs.

In the ditamap file, you reference each topic you want included like this:

<topichead navtitle="Links">
 <topicref href="keyref_links.dita"/>
 <topicref href="inline_links.dita" audience="field_engineers"/>
 <topicref href="related_links.dita"/>
 <topicref href="relationship_tables.dita"/>

Notice how you can add attributes to the topicref elements. Here, if the build conditions exclude the field engineer audience, the inline_links.dita topic will be excluded.

Additionally, hierarchy is established by nesting the elements. If you want to create sublevels, you nest the topics like this:

<topichead navtitle="Links">
 <topicref href="keyref_links.dita">
   <topicref href="inline_links.dita" audience="field_engineers"/>
 <topicref href="related_links.dita">
   <topicref href="relationship_tables.dita"/>

Here the inline_links.dita topic will appear in a sublevel below keyref_links.dita. Similarly, relationship_tables.dita will appear nested below related_links.dita.

You can combine topics if you use the chunk attribute, like this:

 <topicref href="inserting_links.dita" chunk="to-content">
 <topicref href="inline_links.dita" toc="no"/>
 <topicref href="related_links.dita" toc="no"/>
 <topicref href="keyref_links.dita" toc="no"/>

Now the inserting_links.dita topic will contain inline_links.dita, related_links.dita, and keyref_links.dita files as additional sections in the same inserting_links.dita topic, rather than appearing as separate files.

If you want to include any non-DITA files, you would list them in the ditamap with a special processing-role attribute:

<topicref processing-role="resource-only" href="notes.dita" />

For more details on ditamap files, see my page on ditamap files.

Creating a TOC with Jekyll via categories

Jekyll provides a lot of different ways to create a TOC. Since many Jekyll sites are blogs, more emphasis seems to be given to post sorting than page sorting, but Jekyll also provides an interesting paradigm to handle the TOC for pages.

You have at least two main options for create a TOC. You can add a category and weight as frontmatter in your page, and then run for loops to sort the content based on the category and weight.

If you want to sort pages by category, you would put a category in the frontmatter of a page, like this:

title: My Page
permalink: /mypage/
category: getting_started
weight: 1

You could then use a for loop to get all pages in this category sorted by weight:

{% for page in site.pages | sort: weight %}
{% for tag in page.tags %}
 {% if tag == "getting_started" %}
    <li><a href="{{ page.permalink | prepend: site.baseurl }}">{{page.title}}</a></li>
   {% endif %}
  {% endfor %}
{% endfor %}

This approach may get tedious, though, especially if you want to easily shift and arrange your TOC without opening every single page to change these frontmatter values.

Creating a TOC in Jekyll via data files

Another approach, and the one I'm currently using, is to list your TOC entries in a YML data file. Here is the typical structure of a YML file containing TOC entries. The YML syntax emphasizes human-readable code, so a lot of the formatting is dependent on spacing:

- title: Sidebar
    - title: Overview
        - title: Introduction
          url: /introduction/
          audience: customer</p>
<p>        - title: Release Notes
          url: /release_notes/
          audience: customer, fe</p>
<p>        - title: High-level Summary
          url: /high_level_summary_customer/
          audience: customer</p>
<p>        - title: High-level Summary
          url: /high_level_summary_fe/
          audience: fe</p>
<p>    - title: Configuration
        - title: Configuration overview
          url: /configuration_overview/
        - title: "Configuration Details: First Steps"
          url: /configuration_details/

Now the HTML code in your template uses a for loop to iterate through each of these items and wrap them in the right formatting for your theme. Here's an example of how the template code might look:

{% if site.audience == "fe" or "customer" %}
{% assign sidebar = %}
{% assign buildAudience = "fe" %}</p>
<p>{% elsif site.audience == "operations" %}
{% assign sidebar = %}
{% assign buildAudience = "ops" %}
{% endif %}</p>
<p><ul id="mysidebar" class="nav">
{% for entry in sidebar %}
  {% for subcategory in entry.subcategories %}
    {% if subcategory.audience contains buildAudience %}
       <li><a href="#">{{ subcategory.title }}</a>
           {% for item in subcategory.items %}
              {% if item.audience contains buildAudience %}
                <li><a href="{{item.url | prepend: site.baseurl}}">{{item.title}}</a></li>
              {% endif %}
          {% endfor %}
   {% endif %}
 {% endfor %}
{% endfor %}

When we start the for loop, what sidebar refer and buildAudience refer to is based on the assignments defined. These assignments depend on properties in the particular configuration file that's being used. (This is how the theme is separated from the content.)

The for loop looks in the sidebar list and loops first through the items, and then the subcategories, iterating through each item in the list. Each item is wrapped in a link format. Additionally, only the items containing the buildAudience attributes are included in the list. (This buildAudience element allows us to single source the data file, using the same data file for multiple audiences.)

You can get more fancy with the sidebar formatting, such as showing active links and more. But this shows the basic logic. My intent here is not to explain the code in detail but rather to give you a glimpse of how to build a TOC in Jekyll.

Note also that the logic seems more complex here, but I've taken the explanation one level further than I did with DITA. With the DITA TOC explanation, I didn't get into how the Open Toolkit (OT) processes the ditamap file to render it into HTML and other outputs. I actually have no clue how the OT does the processing -- that's usually a behind-the-scenes process.

With Jekyll, though, once you understand how for loops iterate through items in a YML list, it's fairly accessible to configure how the TOC items get processed.

One important difference with Jekyll is that all content is included by default, whether it appears in the navigation or not. If you want to exclude content, you must list it in the Excludes property in your configuration file.

Multiple types of navigation

The ditamap structure tends to break down when your content resembles more of a knowledge base than a user guide. Because you can't tag pages or add pages to specific categories, about the only way to add DITA files to a knowledge-base-style output is by creating an enormous TOC or by not creating a TOC at all, but instead just relying on search.

In contrast, with a Jekyll project, I could create both a TOC-style navigation for hierarchical content, and a tag-based navigation for knowledge-base style content.

Dynamic assembly of content

One strength of DITA is the ability to dynamically assemble larger topics from smaller ones, but this might also be seen as a weakness. In OxygenXML's webhelp output, if you use the chunk attribute to glue a topic together at build time, you can still find the individual chunks in search (they're only glued together in the TOC, not physically in the file contents).

These individual chunks are problematic -- they don't make much sense out of context from the larger topic they were chunked into. (You might think the chunk attribute permanently glues them together, but that magic only happens in the TOC.)

Additionally, if you have a relationship table in a "chunked" topic (meaning a topic that is dynamically assembled through the chunk attribute), the relationship table appears (oddly enough) after the first chunk, and then the other sections appear (see this post for more detail).

This odd behavior of the Open Toolkit is necessary (I think) so that the relationship table can appear at the bottom of the all the individual chunks that don't appear glued together in the TOC topic but rather surface independently, for example, through search.

If you're going to include a topic in another topic, there's no reason not to use a conref from within the topic itself, or if using Jekyll, to use includes from within the topic.


Overall, I hope to have shown that Jekyll provides some robust options when it comes to creating your table of contents. What's especially important is that the TOC content is not merged into the theme in a tight way. Content is separated from format (stored in a data file) and merged together at build time with your theme's template.

follow us in feedly

Get new posts delivered straight to your inbox.

Subscriber count: 3,725

About Tom Johnson

Tom Johnson

I'm a technical writer based in the California San Francisco Bay area. Topics I write about on this blog include technical writing, authoring and publishing tools, API documentation, tech comm trends, visual communication, technical writing career advice, information architecture and findability, developer documentation, and more. If you're a professional or aspiring technical writer, be sure to subscribe to email updates using the form above. You can learn more about me here.