ssg  
Article Series

This article series contain few sections.


Obsolete Repository:
This article use obsolete repository. Please refer to below repository for a more recent articles.

Complete Repository

Where to Discuss?

Local Group

Preface

Goal: Add indicator, and putting all pagination part together.

Preview

This is what we want to achieve in this tutorial.

Hugo Pagination: Combined Animation

More Recent Code

I have made a better code with Bulma utilizing site.pagination.permalink which you can examine at:


1: Prepare

Artefact that we need.

Source

I respect copyright. Most of the code below, especially middle pagination, ported from Hugo Chroma to Jekyll Liquid:

The rest is my custom modification.

Includes: Pagination: Combined

Create an empty artefact, to combine adjacent and indicator. Source code used in this tutorial, is available at this repository:

You should have this minimal code, before you begin.

{% capture spaceless %}
  ...
{% endcapture %}

<nav aria-label="Page navigation">

  {% if total_pages > 1 %}
  <ul class="pagination justify-content-center">

  ...

  </ul>
  {% endif %}

</nav>

Pages: Blog List

Set this blog list page:

  {% include pagination/04-indicator.html %}

We will achieve this with Jekyll code.


2: Preview: General

Structure

This consist of at least seven parts:

  • Previous Page: «

  • First Page: always 1

  • Left Indicator

  • Middle Pagination: Glenn McComb

  • Right Indicator

  • Last Page: always the same number

  • Next Page: »

We will not discuss about Middle Pagination, as it has already been discussed in previous article.

HTML Preview

The HTML that we want to achieve is similar as below.

  <ul class="pagination justify-content-center">
      <li class="page-item blog_previous">...</li>
      <li class="page-item first">...</li>
      <li class="pages-indicator first disabled">...</li>

      <li class="page-item">...</li>
      <li class="page-item">...</li>
      <li class="page-item active ">...</li>
      <li class="page-item">...</li>
      <li class="page-item">...</li>

      <li class="pages-indicator last disabled">...</li>
      <li class="page-item last">...</li>
      <li class="page-item blog_next">...</li>
  </ul>

Small Preview

This is the complete version.

Hugo Pagination: Combined Animation

Wide Preview

Wide version, in responsive context, is slightly different.

Hugo Pagination: Preview Wide

We will use responsive CSS later to achieve this effect.

Partial: Pagination Code Skeleton

As usual, the skeleton, to show the complexity.

<nav aria-label="Page navigation">
{% capture spaceless %}
  <!-- Variable Initialization. -->
{% endcapture %}

  {% if total_pages > 1 %}
  <ul class="pagination justify-content-center">

    <!-- Previous Page. -->
    <!-- First Page. -->
    <!-- Early (More Pages) Indicator. -->

    {% for page in (1..total_pages) %}
      <!-- Flag Calculation -->
      {% assign page_current_flag = false %}

      {% if total_pages > link_max %}
        <!-- Complex page numbers. -->
      
          <!-- Lower limit pages. -->
          <!-- Upper limit pages. -->
          <!-- Middle pages. -->

      {% else %}
        <!-- Simple page numbers. -->
      {% endif %}

      {% if page_current_flag == true %}
        <!-- Show Pager. -->
      {% endif %}
    {% endfor %}

    <!-- Late (More Pages) Indicator. -->
    <!-- Last Page. -->
    <!-- Next Page. -->

  </ul>
  {% endif %}

</nav>

Each Pagination

Consider again, have a look at the animation above, frame by frame.

We have from first page (1), to last page (10).

Hugo Pagination: Adjacent Page 1

Hugo Pagination: Adjacent Page 2

Hugo Pagination: Adjacent Page 3

Hugo Pagination: Adjacent Page 4

Hugo Pagination: Adjacent Page 5

Hugo Pagination: Adjacent Page 6

Hugo Pagination: Adjacent Page 7

Hugo Pagination: Adjacent Page 8

Hugo Pagination: Adjacent Page 9

Hugo Pagination: Adjacent Page 10


3: Variables: Initialization

Consider refresh our memory, about our variables initialization.

{% capture spaceless %}

  {% comment %}
  Pagination links 
  * https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
  {% endcomment %}

  {% if page.paginate_root == nil %}
    {% assign paginate_root = "/" %}
  {% else %}    
    {% assign paginate_root = page.paginate_root %}
  {% endif %}

  {% assign total_pages   = paginator.total_pages %}
  {% assign page_current  = paginator.page %}

  {% assign link_offset   = 2 %}  
  {% assign link_max      = link_offset | times: 2 | plus: 1 %}
  
  {% assign limit_lower   = link_offset | plus: 1 %}
  {% assign limit_upper   = total_pages  | minus: link_offset %}
  
  {% assign min_lower     = link_max %}  
  {% assign max_upper     = total_pages | minus: link_max %}
    
  {% assign lower_offset  = page_current | minus: link_offset %}
  {% assign upper_offset  = page_current | plus: link_offset %}

  {% assign lower_indicator = 2 %}
  {% assign upper_indicator = total_pages | minus: 1 %}

{% endcapture %}

4: Navigation: Previous and Next

It is similar to our simple pagination. Except that, now we use dictionary.

    <!-- Previous Page. -->
    {% if paginator.previous_page %}
      <li class="page-item blog_previous">
        <a class="page-link" 
           href="{{ site.baseurl }}{{ paginator.previous_page_path }}"
           rel="prev">&laquo;</a>
      </li>
    {% else %}
      <li class="page-item blog_previous disabled">
        <span class="page-link">&laquo;</span>
      </li>
    {% endif %}
    <!-- Next Page. -->
    {% if paginator.next_page %}
      <li class="page-item blog_next">
        <a class="page-link" 
           href="{{ site.baseurl }}{{ paginator.next_page_path }}" 
           rel="next">&raquo;</a>
      </li>
    {% else %}
      <li class="page-item blog_next disabled">
        <span class="page-link">&raquo;</span>
      </li>
    {% endif %}

5: Navigation: First and Last

It is different to our simple pagination. Although it is based on the same logic.

This will not be shown, if it is already be shown middle pagination.

    {% if total_pages > link_max %}
      <!-- First Page. -->
      {% if lower_offset > 1 %}
        <li class="page-item first">
          <a class="page-link"
             href="{{ site.baseurl }}{{ page.paginate_root }}">1</a>
        </li>
      {% endif %}
    {% endif %}

This will not be shown, if it is already be shown middle pagination.

    {% if total_pages > link_max %}
      <!-- Last Page. -->
      {% if upper_offset < total_pages %}
        <li class="page-item last">
          <a class="page-link" 
             href="{{ site.paginate_path | relative_url | replace: ':num', total_pages }}"
          >{{ total_pages }}</a>
        </li>
      {% endif %}
    {% endif %}

6: Indicator: Left and Right

This will only be shown, if necessary.

Indicator: Left

    {% if total_pages > link_max %}
      <!-- Early (More Pages) Indicator. -->
      {% if lower_offset > lower_indicator %}
        <li class="pages-indicator first disabled">
          <span class="page-link">...</span>
        </li>
      {% endif %}
    {% endif %}

Indicator: Right

    {% if total_pages > link_max %}
      <!-- Late (More Pages) Indicator. -->
      {% if upper_offset < upper_indicator %}
        <li class="pages-indicator last disabled">
          <span class="page-link">...</span>
        </li>
      {% endif %}
    {% endif %}

8: Combined Code

It is about the right time to put all the code together.

Hugo Pagination: Combined Animation

{% capture spaceless %}

  {% comment %}
  Pagination links 
  * https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
  {% endcomment %}

  {% if page.paginate_root == nil %}
    {% assign paginate_root = "/" %}
  {% else %}    
    {% assign paginate_root = page.paginate_root %}
  {% endif %}

  {% assign total_pages   = paginator.total_pages %}
  {% assign page_current  = paginator.page %}

  {% assign link_offset   = 2 %}  
  {% assign link_max      = link_offset | times: 2 | plus: 1 %}
  
  {% assign limit_lower   = link_offset | plus: 1 %}
  {% assign limit_upper   = total_pages  | minus: link_offset %}
  
  {% assign min_lower     = link_max %}  
  {% assign max_upper     = total_pages | minus: link_max %}
    
  {% assign lower_offset  = page_current | minus: link_offset %}
  {% assign upper_offset  = page_current | plus: link_offset %}

  {% assign lower_indicator = 2 %}
  {% assign upper_indicator = total_pages | minus: 1 %}

{% endcapture %}

<nav aria-label="Page navigation">

  {% if total_pages > 1 %}
  <ul class="pagination justify-content-center">
    <!-- Previous Page. -->
    {% if paginator.previous_page %}
      <li class="page-item blog_previous">
        <a class="page-link" 
           href="{{ site.baseurl }}{{ paginator.previous_page_path }}"
           rel="prev">&laquo;</a>
      </li>
    {% else %}
      <li class="page-item blog_previous disabled">
        <span class="page-link">&laquo;</span>
      </li>
    {% endif %}

    {% if total_pages > link_max %}
      <!-- First Page. -->
      {% if lower_offset > 1 %}
        <li class="page-item first">
          <a class="page-link"
             href="{{ site.baseurl }}{{ page.paginate_root }}">1</a>
        </li>
      {% endif %}

      <!-- Early (More Pages) Indicator. -->
      {% if lower_offset > lower_indicator %}
        <li class="pages-indicator first disabled">
          <span class="page-link">...</span>
        </li>
      {% endif %}
    {% endif %}

    <!-- Page numbers. -->
    {% for page in (1..total_pages) %}
    
    {% capture spaceless %}
      {% assign page_current_flag = false %}

      {% if total_pages > link_max %}

        {% if page_current <= limit_lower %}
          {% if page <= min_lower %}
            {% assign page_current_flag = true %}
          {% endif %}

        {% elsif page_current >= limit_upper %}
          {% if page > max_upper %}
            {% assign page_current_flag = true %}
          {% endif %}

        {% else %}
          
          {% if (page >= lower_offset) and (page <= upper_offset) %}
            {% assign page_current_flag = true %}
          {% endif %}

        {% endif %}

      {% else %}
      
        {% assign page_current_flag = true %}
      {% endif %}
    {% endcapture %}
    
      <!-- Show Pager. -->
      {% if page_current_flag == true %}
      <li class="page-item {% if page == page_current %} active{% endif %}">
        {% if page == page_current %} 
          <span class="page-link">
            {{ page }}
          </span>
        {% elsif page == 1 %}
          <a class="page-link"
             href="{{ site.baseurl }}{{ paginate_root }}"
           >1</a>
        {% else %}
          <a class="page-link"
             href="{{ site.paginate_path | relative_url | replace: ':num', page }}"
           >{{ page }}
          </a>
        {% endif %}
      </li>
      {% endif %}
    {% endfor %}

    {% if total_pages > link_max %}
      <!-- Late (More Pages) Indicator. -->
      {% if upper_offset < upper_indicator %}
        <li class="pages-indicator last disabled">
          <span class="page-link">...</span>
        </li>
      {% endif %}

      <!-- Last Page. -->
      {% if upper_offset < total_pages %}
        <li class="page-item last">
          <a class="page-link" 
             href="{{ site.paginate_path | relative_url | replace: ':num', total_pages }}"
          >{{ total_pages }}</a>
        </li>
      {% endif %}
    {% endif %}

    <!-- Next Page. -->
    {% if paginator.next_page %}
      <li class="page-item blog_next">
        <a class="page-link" 
           href="{{ site.baseurl }}{{ paginator.next_page_path }}" 
           rel="next">&raquo;</a>
      </li>
    {% else %}
      <li class="page-item blog_next disabled">
        <span class="page-link">&raquo;</span>
      </li>
    {% endif %}
  </ul>
  {% endif %}

</nav>

Pretty long code, sure.


9: Responsive: Simple

Before we continue to the next pagination article about responsive, consider having this very simple example using bootstrap.

Hugo Pagination: Preview Wide

This will show the word previous and next for medium page.

SASS: Custom Pagination

I’m using Bootstrap 4 grid breakpoints.

@include media-breakpoint-up(md) {

  .blog_previous {
    span.page-link:after,
    a.page-link:after {
      content: " previous"
    }
  }

  .blog_next {
    span.page-link:before,
    a.page-link:before {
      content: "next "
    }
  }

}

SASS: Main

@import
  // variables
    "vendors/bootstrap/functions",
    "variables",
    "vendors/bootstrap/variables",
    "vendors/bootstrap/mixins/breakpoints",
    "vendors/bootstrap/mixins/image",
    "vendors/bootstrap/mixins/gradients",
    "vendors/bootstrap/mixins/transition",
    "vendors/bootstrap/mixins/box-shadow",

  // taken from bootstrap
    "sticky-footer-navbar",
    "bootstrap-custom",

  // custom: general
    "layout",
    "decoration",
    "stripes",
    "list",
    "pagination",

  // custom: post
    "post-navigation",
    "post-calendar",
    "post-header",
    "post-content",
    "post-code",
    "post-highlight-jekyll"
;

SASS: Bootstrap Grid Breakpoint Variables.

Bootstrap 4 grid breakpoints are defined as below.

  • _sass/vendors/bootstrap/_variables.scss
// Grid breakpoints
//
// Define the minimum dimensions at which your layout will change,
// adapting to different screen sizes, for use in media queries.

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;

What is Next ?

Consider continue reading [ Jekyll Pagination - Responsive ].

Thank you for reading.