Jekyll Themes: Complete Usage Guide

Jekyll themes provide a powerful way to create consistent, maintainable websites while separating content from presentation. This comprehensive guide covers everything from basic theme usage to advanced API integration patterns.

Table of Contents

  1. Understanding Jekyll Themes
  2. Theme Installation Methods
  3. Theme Structure and Components
  4. Customizing Themes
  5. Advanced Theme Development
  6. Creating Help APIs with Themes
  7. Collections and Data Integration
  8. Theme Best Practices
  9. Troubleshooting Common Issues
  10. Resources and References

Understanding Jekyll Themes

What Are Jekyll Themes?

Jekyll themes are reusable packages that contain:

Theme Types

Gem-based Themes

# _config.yml
theme: minima

Fork-based Themes

GitHub Pages Compatible Themes

# _config.yml
remote_theme: jekyll/minima

Theme Installation Methods

Method 1: Gem-based Installation

Step 1: Add theme to Gemfile

# Gemfile
gem "theme-name", "~> 1.0"

Step 2: Update configuration

# _config.yml
theme: theme-name

Step 3: Install and build

bundle install
bundle exec jekyll serve

Method 2: Remote Theme (GitHub Pages)

# _config.yml
remote_theme: username/theme-repository
plugins:
  - jekyll-remote-theme

Method 3: Fork and Customize

# Clone theme repository
git clone https://github.com/username/theme-name.git my-site
cd my-site

# Install dependencies
bundle install

# Customize and build
bundle exec jekyll serve

Theme Structure and Components

Essential Theme Files

theme-name/
├── _layouts/
│   ├── default.html      # Base layout
│   ├── page.html         # Page layout
│   ├── post.html         # Blog post layout
│   └── home.html         # Homepage layout
├── _includes/
│   ├── header.html       # Site header
│   ├── footer.html       # Site footer
│   ├── head.html         # HTML head section
│   └── navigation.html   # Navigation menu
├── _sass/
│   ├── _variables.scss   # Theme variables
│   ├── _base.scss        # Base styles
│   └── _layout.scss      # Layout styles
├── assets/
│   ├── main.scss         # Main stylesheet
│   ├── js/              # JavaScript files
│   └── images/          # Theme images
├── _config.yml          # Theme configuration
└── theme-name.gemspec   # Gem specification

Layout Hierarchy

---
layout: default # Inherits from default.html
---

Layout inheritance example:

<!-- _layouts/default.html -->
<!DOCTYPE html>
<html>
  <head>
    <% include head.html %%}gt;
  </head>
  <body>
    <% include header.html %%}gt;
    <main><{{lt; content >}}gt;</main>
    <% include footer.html %%}gt;
  </body>
</html>

<!-- _layouts/post.html -->
---
layout: default
---
<article class="post">
  <h1><{{lt; page.title >}}gt;</h1>
  <div class="post-content"><{{lt; content >}}gt;</div>
</article>

Customizing Themes

Override Theme Files

Create files with the same path in your site to override theme files:

your-site/
├── _layouts/
│   └── post.html         # Overrides theme's post.html
├── _includes/
│   └── header.html       # Overrides theme's header.html
└── _sass/
    └── _variables.scss   # Overrides theme variables

Customize Variables

// _sass/_variables.scss
$primary-color: #2c5aa0;
$font-family: 'Helvetica Neue', Arial, sans-serif;
$content-width: 800px;

// Import theme styles after variables
@import 'theme-name';

Add Custom CSS

// assets/main.scss
---
---
@import "theme-name";

// Custom styles
.custom-section {
  background-color: var(--custom-bg);
  padding: 2rem;
}

Configuration Customization

# _config.yml
title: 'My Custom Site'
description: 'Powered by Jekyll Theme'

# Theme-specific settings
theme_settings:
  navigation:
    - title: 'Home'
      url: '/'
    - title: 'About'
      url: '/about/'

  social_links:
    github: 'username'
    twitter: 'username'

# Override theme defaults
permalink: /:categories/:year/:month/:day/:title/
paginate: 10

Advanced Theme Development

Creating Custom Layouts

<!-- _layouts/api-docs.html -->
---
layout: default
---
<div class="api-documentation">
  <aside class="api-sidebar"><!-- <% include api-navigation.html %%}gt; --></aside>

  <main class="api-content">
    <header class="api-header">
      <h1><{{lt; page.title >}}gt;</h1>
      <% if page.api_version %%}gt;
      <span class="api-version"><{{lt; page.api_version >}}gt;</span>
      <% endif %%}gt;
    </header>

    <div class="api-body"><{{lt; content >}}gt;</div>

    <% if page.code_examples %%}gt;
    <section class="code-examples">
      <% for example in page.code_examples %%}gt;
      <div class="code-example">
        <h3><{{lt; example.title >}}gt;</h3>
        <pre><code class="<{{lt; example.language >}}gt;"><{{lt; example.code >}}gt;</code></pre>
      </div>
      <% endfor %%}gt;
    </section>
    <% endif %%}gt;
  </main>
</div>

Theme Configuration Schema

# theme-name.gemspec
Gem::Specification.new do |spec|
  spec.name          = "theme-name"
  spec.version       = "1.0.0"
  spec.authors       = ["Your Name"]
  spec.email         = ["your.email@example.com"]

  spec.summary       = "A Jekyll theme for documentation"
  spec.homepage      = "https://github.com/username/theme-name"
  spec.license       = "MIT"

  spec.files         = `git ls-files -z`.split("\x0").select do |f|
    f.match(%r{^(assets|_layouts|_includes|_sass|LICENSE|README)}i)
  end

  spec.add_runtime_dependency "jekyll", "~> 4.0"
  spec.add_runtime_dependency "jekyll-feed", "~> 0.9"
  spec.add_runtime_dependency "jekyll-sitemap", "~> 1.0"
end

Creating Help APIs with Themes

Setting Up Collections for API Content

# _config.yml
collections:
  tooltips:
    output: false
  api_docs:
    output: true
    permalink: /:collection/:name/

Creating Tooltip Definitions

# _data/definitions.yml
api_key: 'A unique identifier used to authenticate API requests. Keep this secure and never expose it in client-side code.'

response_codes: 'HTTP status codes returned by the API. 200 indicates success, 4xx indicates client errors, 5xx indicates server errors.'

rate_limiting: 'The practice of limiting the number of API requests a client can make within a specific time period to prevent abuse and ensure fair usage.'

Tooltip Collection Pages

<!-- _tooltips/api-key.md -->
---
doc_id: api_key
product: mijug_api
category: authentication
---

<{{lt; site.data.definitions.api_key >}}gt;

JSON API Generation

---
layout: null
search: exclude
---
{
  "entries": [
    <% for page in site.tooltips %%}gt;
      <% if page.product == "mijug_api" %%}gt;
        {
          "doc_id": "<{{lt; page.doc_id >}}gt;",
          "category": "<{{lt; page.category >}}gt;",
          "body": "<{{lt; page.content | strip_newlines | replace: '\', '\\\\' | replace: '"', '\\"' >}}gt;"
        }<% unless forloop.last %%}gt;,<% endunless %%}gt;
      <% endif %%}gt;
    <% endfor %%}gt;
  ],
  "metadata": {
    "generated": "<{{lt; site.time | date_to_xmlschema >}}gt;",
    "version": "<{{lt; site.data.api.version >}}gt;",
    "total_entries": <{{lt; site.tooltips | where: "product", "mijug_api" | size >}}gt;
  }
}

Advanced API Integration

// assets/js/theme-api.js
class ThemeAPIHelper {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
    this.cache = new Map();
  }

  async loadTooltips() {
    if (this.cache.has('tooltips')) {
      return this.cache.get('tooltips');
    }

    try {
      const response = await fetch(`${this.apiUrl}/tooltips.json`);
      const data = await response.json();
      this.cache.set('tooltips', data);
      return data;
    } catch (error) {
      console.error('Failed to load tooltips:', error);
      return { entries: [] };
    }
  }

  async initializeTooltips() {
    const data = await this.loadTooltips();

    data.entries.forEach(entry => {
      const elements = document.querySelectorAll(
        `[data-tooltip="${entry.doc_id}"]`
      );
      elements.forEach(element => {
        this.attachTooltip(element, entry.body);
      });
    });
  }

  attachTooltip(element, content) {
    // Bootstrap popover integration
    if (typeof bootstrap !== 'undefined') {
      new bootstrap.Popover(element, {
        content: content,
        html: true,
        placement: 'top',
        trigger: 'hover focus',
      });
    }
  }
}

// Initialize API helper
document.addEventListener('DOMContentLoaded', () => {
  const apiHelper = new ThemeAPIHelper('/api');
  apiHelper.initializeTooltips();
});

Collections and Data Integration

Multi-language Support

# _config.yml
collections:
  docs_en:
    output: true
    permalink: /en/:name/
  docs_es:
    output: true
    permalink: /es/:name/

defaults:
  - scope:
      path: '_docs_en'
    values:
      lang: 'en'
      layout: 'docs'
  - scope:
      path: '_docs_es'
    values:
      lang: 'es'
      layout: 'docs'

Data-driven Navigation

# _data/navigation.yml
main:
  - title: 'Documentation'
    url: '/docs/'
    children:
      - title: 'Getting Started'
        url: '/docs/getting-started/'
      - title: 'API Reference'
        url: '/docs/api/'
        children:
          - title: 'Authentication'
            url: '/docs/api/auth/'
          - title: 'Endpoints'
            url: '/docs/api/endpoints/'

footer:
  - title: 'Resources'
    links:
      - title: 'GitHub'
        url: 'https://github.com/username/repo'
      - title: 'Issues'
        url: 'https://github.com/username/repo/issues'

Dynamic Content Generation

<!-- _includes/api-navigation.html -->
<nav class="api-navigation">
  <% assign api_pages = site.api_docs | sort: 'order' %%}gt;
  <% for page in api_pages %%}gt;
    <div class="nav-section">
      <h3><{{lt; page.section >}}gt;</h3>
      <ul>
        <% assign section_pages = site.api_docs | where: 'section', page.section | sort: 'order' %%}gt;
        <% for section_page in section_pages %%}gt;
          <li>
            <a href="<{{lt; section_page.url | relative_url >}}gt;"
               <% if section_page.url == page.url %%}gt;class="active"<% endif %%}gt;>
              <{{lt; section_page.title >}}gt;
            </a>
          </li>
        <% endfor %%}gt;
      </ul>
    </div>
  <% endfor %%}gt;
</nav>

Theme Best Practices

Performance Optimization

// Optimize CSS loading
@import "variables";
@import "mixins";

// Load critical styles first
@import "base";
@import "layout";

// Load component styles conditionally
<% if page.layout == "post" %%}gt;
  @import "post";
<% endif %%}gt;

Accessibility Standards

<!-- _includes/accessible-navigation.html -->
<nav role="navigation" aria-label="Main navigation">
  <button
    class="nav-toggle"
    aria-expanded="false"
    aria-controls="nav-menu"
    aria-label="Toggle navigation menu"
  >
    <span class="sr-only">Menu</span>
  </button>

  <ul id="nav-menu" class="nav-menu">
    <% for item in site.data.navigation.main %%}gt;
    <li>
      <a
        href="<{{lt; item.url | relative_url >}}gt;"
        <% if item.url == page.url %%}gt;aria-current="page"<% endif %%}gt;
      >
        <{{lt; item.title >}}gt;
      </a>
    </li>
    <% endfor %%}gt;
  </ul>
</nav>

SEO Integration

<!-- _includes/seo.html -->
<meta
  name="description"
  content="<{{lt; page.description | default: site.description | escape >}}gt;"
/>
<meta name="keywords" content="<{{lt; page.tags | join: ', ' >}}gt;" />

<!-- Open Graph -->
<meta property="og:title" content="<{{lt; page.title | default: site.title >}}gt;" />
<meta
  property="og:description"
  content="<{{lt; page.description | default: site.description >}}gt;"
/>
<meta property="og:url" content="<{{lt; page.url | absolute_url >}}gt;" />

<!-- Schema.org -->
<script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "<{{lt; page.schema_type | default: 'WebPage' >}}gt;",
    "name": "<{{lt; page.title | escape >}}gt;",
    "description": "<{{lt; page.description | escape >}}gt;"
  }
</script>

Cross-Origin Resource Sharing (CORS)

For themes that serve APIs or data files:

# .htaccess
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"

Troubleshooting Common Issues

Theme Not Loading

Problem: Theme styles not applying

# Check if theme is installed
bundle list | grep theme-name

# Reinstall theme
bundle clean --force
bundle install

Solution: Verify theme configuration

# _config.yml
theme: correct-theme-name # Check spelling

Override Not Working

Problem: Custom files not overriding theme files

# Check file paths match exactly
bundle info theme-name --path

Solution: Ensure correct directory structure

your-site/
├── _layouts/
│   └── post.html        # Must match theme path exactly

Build Errors

Problem: Liquid syntax errors

Liquid Exception: undefined method `title' for nil:NilClass

Solution: Add null checks

<% if page.title %%}gt;
  <h1><{{lt; page.title >}}gt;</h1>
<% endif %%}gt;

Performance Issues

Problem: Slow build times

# _config.yml
incremental: true
profile: true

Solution: Optimize loops and includes

<!-- Cache expensive operations -->
<% assign sorted_posts = site.posts | sort: 'date' | reverse %%}gt;
<% for post in sorted_posts limit: 10 %%}gt;
  <!-- Content -->
<% endfor %%}gt;

Resources and References

Official Documentation

Popular Themes

Development Tools

Community Resources


Conclusion

Jekyll themes provide a powerful foundation for creating maintainable, scalable websites. By understanding theme structure, customization patterns, and advanced features like API integration, you can create sophisticated documentation sites that serve both human readers and automated systems.

The integration of help APIs through collections and JSON generation allows themes to extend beyond static content, creating dynamic, interactive experiences while maintaining the simplicity and performance benefits of static site generation.

Whether you're using an existing theme or developing your own, following these best practices will ensure your Jekyll site is accessible, performant, and maintainable for years to come.


Bibliography

Primary Sources

  1. Jekyll Official Documentation
  2. Liquid Template Language

API Integration References

  1. MyDoc Help API Documentation
  2. Web Standards and Accessibility

Theme Development Resources

  1. Popular Jekyll Themes
  2. Development Tools and Validation

Community and Support

  1. Jekyll Community Resources

Technical Standards

  1. Web Performance and Security

Citation Format

This document follows the Chicago Manual of Style for web resources. All URLs were verified as accessible on July 31, 2025. For the most current information, please refer to the original sources as web content may change over time.

Special Acknowledgment: The API integration patterns and collection-based help system concepts in this guide are significantly influenced by the MyDoc Help API documentation by SuperScary, which provides excellent examples of Jekyll-based API generation and tooltip systems.


This guide is part of the MIJUG.NET documentation series. For updates and additional resources, visit MIJUG.NET.