Making Changes to HTML Version

The Solr Ref Guide uses Jekyll to build the HTML version of the site.

What is Jekyll?

Jekyll is a static site generator, meaning that it takes some set of documents and produces HTML pages. It allows for templating of the pages, so each page has the same look and feel without having to code headers, footers, logos, etc., into every page.

Jekyll is an open source project written in Ruby, online at https://jekyllrb.com/.

How We Use Jekyll

The following sections describe the main features of Jekyll that you will encounter while working with the Solr Ref Guide.

Jekyll-Asciidoctor Plugin

We use a plugin for Jekyll from the Asciidoctor project to integrate Jekyll with Asciidoc formatted content. The source for the plugin is available at https://github.com/asciidoctor/jekyll-asciidoc.

This plugin allows us to use Asciidoctor-style variables with Jekyll, instead of having to maintain two sets of the same variables (one for HTML version and another for PDF version).

_config.yml

The _config.yml is a global configuration file that drives many of the options used when building the site (particularly in our use of Jekyll).

We have template-ized _config.yml, so in our use of Jekyll you will find it in solr/solr-ref-guide/src as _config.yml.template. This allows us to define some variables during the build and use common Lucene/Solr build parameters (such as project versions).

Front Matter

Front matter for Jekyll is similar to a header that defines the title of the page, and other variables that may be helpful (or even required) when rendering the page.

Every document that will be converted to HTML must include at least the page title at the top of the page. Page titles are defined with a single equal sign (=) followed by the title that will appear at the top of the page (such as = Topic of the Page).

Many guides to Jekyll also say that defining the layout in the front matter is required. However, since we only use one layout for all pages, we have defined this as a default.

The Solr Ref Guide uses the front matter to define some custom attributes on a per page basis:

  • page-children - ordered list of child pages, this is used to build the site navigation menu that appears to the left of each page’s content (and to order the pages in the PDF)

Other page-level elements can also be defined, such as an Asciidoctor attribute that should apply only to that page, but are not needed on a regular basis.

The format for adding any parameter to the front matter is to use colons on both sides of the parameter, followed by the value for the parameter (such as :page-toc: false).

Table of Contents

There are some optional custom attributes that can be defined in pages to affect the Table of Contents presentation in Jekyll:

page-toclevels

Changes how "deep" the TOC will be in terms of nested section/sub-section titles (default = 2).

page-tocclass

Changes the CSS class applied to the TOC. The options are:

  • normal, the default, resulting in the class name toc-normal being used in the HTML.

  • right, to put the TOC on the right side of the page.

  • column, to make a very long TOC span 3 columns at the top of the page.

page-toc

If this is false, then no TOCs will be generated for the page at all. The default is true, so can usually be left undefined.

{section-toc}

A special macro that can be used anywhere in a page to create an "In this Section" TOC covering only the sub-headings in the same section.

Setting :page-toc: false will prevent the section-toc macro from working, so if you want no "top level" TOC, but you do want section TOCs, use :page-toclevels: 0.

Layouts

Layouts define the "look and feel" of each page. Jekyll uses Liquid for page templates.

For our implementation of Jekyll, layouts are found in solr-ref-guide/src/_layouts.

We currently use only the _layouts/default.html layout for overall page structure, and _layouts/page.html for the page-level content. The page.html layout is inserted into the default.html layout.

Includes

Include files are (usually) small files that are pulled into a layout when a page is being built. They are Liquid templates that define an area of the page. This allows flexibility across layouts - all pages can have the same header without duplicating code, but different pages could have different menu options.

Include files that we use define the top navigation, the page header, the page footer, and tables of contents.

For our implementation of Jekyll, include files are found in solr-ref-guide/src/_includes.

Data Files

Data files include data such as lists that should be included in each page. The left-hand navigation menu is an example of a data file. However, in our build, the navigation is built from the page-children hierarchies.

For our implementation of Jekyll, data files are found in solr-ref-guide/src/_data.

Using Bootstrap Components

The HTML files include Bootstrap (v3.3.4 as of October 2017, see _includes/head.html to confirm the Bootstrap version currently being used), so all of the components of Bootstrap are available.

However, before trying to use these components, it’s important to understand how Jekyll and Asciidoctor relate to one another.

How Jekyll Consumes .adoc Files

Jekyll creates all of the page elements we do not define in an .adoc file: the header, footer, navigation elements, comments, and other parts of the page that we don’t worry about as we write the content of a page. It produces a template that our content is inserted into.

Asciidoctor converts our content to HTML - defining its own classes to define default styling - and inserts it into the template Jekyll has provided. While we have a lot of control over the CSS for styling, without creating custom plugins for Asciidoctor, we don’t control the HTML elements produced by the plugin.

This is important because in order to use Bootstrap components, we need to insert div classes into the HTML produced by Asciidoctor.

Asciidoctor Roles

Asciidoctor helpfully provides a way to define custom <div> classes in .adoc files, as long as we understand how to use it.

Asciidoctor does not call these "divs" or "classes", but instead "roles". We can give any content a role - to images, content blocks (such as [source] or [NOTE], etc.), even a word in the middle of a sentence.

Because roles are so flexible, they only apply to the thing - the word, content block, image, etc. - they are directly applied to. This means that if we want an entire section of content to be given a specific role in the HTML (i.e., enclosed in a <div>), then we need to put the content in a block.

Creating Tabbed Sections

Hopefully a little bit of background on roles is helpful to understanding the rest of what we’ll do to create a tabbed section in a page.

See the Bootstrap docs on tabs for details on how to use tabs and pills with Bootstrap. As a quick overview, tabs in Bootstrap are defined like this:

<ul class="nav nav-pills"> (1)
  <li class="active"><a data-toggle="pill" href="#sec1">Section 1</a></li>
  <li><a data-toggle="pill" href="#sect2">Section 2</a></li>
</ul>

<div class="tab-content"> (2)
  <div id="sect1" class="tab-pane active"> (3)
    <h3>Section 1</h3>
    <p>Some content.</p>
  </div>
  <div id="sect2" class="tab-pane">
    <h3>Section 2</h3>
    <p>Some other content.</p>
  </div>
</div>
1 This section creates an unordered list with a line item for each tab. The data-toggle and class parameters are what tell Bootstrap how to render the content.
2 Note the class defined here: <div class="tab-content">. This defines that what follows is the content that will make up the panes of our tabs. We will need to define these in our document.
3 In our document, we need to delineate the separate sections of content that will make up each pane.

We have created some custom JavaScript that will do part of the above for us if we assign the proper roles to the blocks of content that we want to appear in the tab panes. To do this, we can use Asciidoctor’s block delimiters to define the tabbed content, and the content between the tab.

  1. Define an "open block" (an unformatted content block), and give it the role .dynamic-tabs. An open block is defined by two hyphens on a line before the content that goes in the block, and two hyphens on a line after the content to end the block. We give a block a role by adding a period before the role name, like this:

    [.dynamic-tabs]
    --
    The stuff we'll put in the tabs will go here.
    --
  2. Next we need to define the content for the tabs between the open block delimiters.

    1. We enclose each tab pane in another type of block, and "example" block. This allows us to include any kind of content in the block and be sure all of the various types of elements (heading, text, examples, etc.) are included in the pane.

    2. We give the example block another role, tab-pane, and we must make sure that each pane has a unique ID. We assign IDs with a hash mark (\#) followed by the ID value (#sect1).

    3. We also need to define a label for each tab. We do this by adding another role, tab-label to the content we want to appear as the name of the tab.

    4. In the end one pane will look like this:

      [example.tab-pane#sect1] (1)
      ==== (2)
      [.tab-label]*Section 1*  (3)
      My content...
      ====
      1 When we define the example block with [example], it’s followed by .tab-pane#sect1 as the class (each class separated by a period .) and the ID defined in the tab definition earlier. Those will become the classes (class="tab-pane active") and ID (id="sect1") in the resulting HTML.
      2 Example blocks are delimited by 4 equal signs (====) before and after the enclosed content.
      3 The words "Section 1" will appear in the HTML page as the label for this tab.
    5. Create [example.tab-pane#id] sections for each tab, until you finally end up with something that looks like this:

      [.dynamic-tabs]
      --
      [example.tab-pane#sect1]
      ====
      [.tab-label]*Section 1*
      My content...
      ====
      
      [example.tab-pane#sect2]
      ====
      [.tab-label]*Section 2*
      My content...
      ====
      --
  3. Because these tabbed sections are created when the HTML and related JavaScript is loaded, when the PDF is generated it will ignore all the class and ID information in the page because it is meaningless to it (asciidoctor-pdf does not recognize roles/custom classes). In the PDF, this content will be displayed in Example Blocks, which have some formatting rules, but will not be shown as tabs and shouldn’t show any other oddities.

Building the HTML Site

An Ant target build-site will build the full HTML site. This target builds the navigation for the left-hand menu, and converts all .adoc files to .html, including navigation and inter-document links.

Building the HTML has several dependencies that will need to be installed on your local machine. Review the README.txt file in the solr/solr-ref-guide directory for specific details.

See Publishing the HTML Version for more information about building.

Build Validation

When you run ant build-site to build the HTML, several additional validations occur during that process. See solr-ref-guide/tools/CheckLinksAndAnchors.java for details of what that tool does to validate content.

Comments on this Page

We welcome feedback on Solr documentation. However, we cannot provide application support via comments. If you need help, please send a message to the Solr User mailing list.