Where to Discuss?

Local Group

Preface

Goal: Bringing screen reader accessability in pagination.

Source Code

You can download the source code of this article here.

Extract and run on CLI using $ npm install.


1: Source

Screen Reader Class

I just follow bulma guidance:

To hidden content visually, you simply need to add is-sr-only.

<span class="is-sr-only">Hidden Content</span>

Alternatively you can use fontawesome class that is similar to bootstrap.

<span class="sr-only">Hidden Content</span>

This content can be read by screenreader. You can test using inspect element.

Aria Label

Instead of class, we can also utilize aria-label.

        <a class="pagination-link is-current" 
           aria-label="Page <%= page.current %>"
           aria-current="true">
          <span class="is-sr-only">Page </span><%= page.current %>
        </a>

2: Prepare

Preview: General

There shoud be nomore preview, because this is screen reader. Luckily, this article meant to provide example of screenreader. So I show all buttons in this template example.

Hexo Pagination: Preview All Buttons

You may freely change the appearance. Just remove any button that you do not need. Change the class, or even the SASS. Just remember that in this article, we should focus on accesability, instead the looks.

Configuration

As usual in Hexo - Pagination - Intro.

Layout: EJS Index

Consider use pagination/06-screenreader layout, in index.ejs


3: Navigation: Previous and Next

Just a slight modification.

    <!-- Previous Page. -->
    <% if (page.prev_link) { %>
      <a class="pagination-previous"
         href="<%= url_for(page.prev_link) %>" 
         aria-label="Go to previous page"
         rel="prev">
        <span class="is-sr-only">Go to </span>
        Previous
        <span class="is-sr-only"> page</span>
      </a>
    <% } else { %>
      <a class="pagination-previous"
         title="This is the first page"
         aria-label="First page"
         aria-current="true"
         disabled>Previous
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>
    <!-- Next Page. -->
    <% if (page.next_link) { %>
      <a class="pagination-next"
         href="<%= url_for(page.next_link) %>" 
         aria-label="Go to next page"
         rel="next">
        <span class="is-sr-only">Go to </span>
        Next
        <span class="is-sr-only"> page</span>
      </a>
    <% } else { %>
      <a class="pagination-next"
         title="This is the last page"
         aria-label="Last page"
         aria-current="true"
         disabled>Next
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>

4: Navigation: First and Last

Just a slight modification.

    <!-- First Page. -->
    <% if (page.prev != 0) { %>
      <a class="pagination-previous"
         href="<%= pagination_url(1) %>" 
         aria-label="Go to first page"
         data-rel="first">
        <span class="is-sr-only">Go to </span>
        First
        <span class="is-sr-only"> page</span>
      </a>
        <% } else { %>
      <a class="pagination-previous"
         title="This is the first page"
         aria-label="First page"
         aria-current="true"
         disabled>First
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>
    <!-- Last Page. -->
    <% if (page.next != 0) { %>
      <a class="pagination-next"
         href="<%= pagination_url(page.total) %>" 
         aria-label="Go to last page"
         data-rel="last">
        <span class="is-sr-only">Go to </span>
        Last
        <span class="is-sr-only"> page</span>
      </a>
    <% } else { %>
      <a class="pagination-next"
         title="This is the last page"
         aria-label="Last page"
         aria-current="true"
         disabled>Last
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>

5: Middle Navigation: Number

      <li class="<%= page_offset_class %>">
        <% if (current != cursor) { %>
        <a href="<%= pagination_url(cursor) %>"
           class="pagination-link" 
           aria-label="Go to page <%= cursor %>">
          <span class="is-sr-only">Go to page </span><%= cursor %>
        </a>
        <% } else { %>
        <a class="pagination-link is-current" 
           aria-label="Page <%= current %>"
           aria-current="true">
          <span class="is-sr-only">Page </span><%= current %>
        </a>
        <% } %>
      </li>

6: Conclusion

Now comes the final part.

Inspect Elements

You can test using screen reader inspect element.

Hexo Pagination: Preview Inspect Elements

Complete Code

As a summary of this pagination tutorial, the complete code is here below:

<%
/*
* Helper function
*/

function pagination_url(number) {
  var path;

  // default for index
  var path = config.index_generator.path;
  
  // dirty quick fix, avoid double (//)
  if (path=='/') { path = '' }
  
  if (is_archive()){
    path = '/' + config.archive_dir;

    if (is_month()){
      // trailing zero
      var month = ( page.month < 10 ? '0' + page.month : page.month );
      path += '/' + page.year + '/' + month;
    } else if (is_year()){
      path += '/' + page.year;
    }
  } else if (is_category()){
    path = '/' + config.category_dir + '/' + page.category;
  } else if (is_tag()){
    path = '/' + config.tag_dir + '/' + page.tag;
  }

  if (number>1) {
      path = path + '/' + config.pagination_dir + '/' + number;
  }

  return url_for(path);
}

/* 
* Pagination links 
* https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
* Adjacent: Number of links either side of the current page
*/

var current        = page.current;
var adjacent_links = 2;
var max_links      = (adjacent_links * 2) + 1;
var lower_limit    = 1 + adjacent_links;
var upper_limit    = page.total - adjacent_links;
%>

<nav class="pagination is-small is-centered" 
     role="navigation" aria-label="pagination">

  <% if (page.total > 1) { %>

    <!-- First Page. -->
    <% if (page.prev != 0) { %>
      <a class="pagination-previous"
         href="<%= pagination_url(1) %>" 
         aria-label="Go to first page"
         data-rel="first">
        <span class="is-sr-only">Go to </span>
        First
        <span class="is-sr-only"> page</span>
      </a>
        <% } else { %>
      <a class="pagination-previous"
         title="This is the first page"
         aria-label="First page"
         aria-current="true"
         disabled>First
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>

    <!-- Previous Page. -->
    <% if (page.prev_link) { %>
      <a class="pagination-previous"
         href="<%= url_for(page.prev_link) %>" 
         aria-label="Go to previous page"
         rel="prev">
        <span class="is-sr-only">Go to </span>
        Previous
        <span class="is-sr-only"> page</span>
      </a>
    <% } else { %>
      <a class="pagination-previous"
         title="This is the first page"
         aria-label="First page"
         aria-current="true"
         disabled>Previous
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>

    <!-- Next Page. -->
    <% if (page.next_link) { %>
      <a class="pagination-next"
         href="<%= url_for(page.next_link) %>" 
         aria-label="Go to next page"
         rel="next">
        <span class="is-sr-only">Go to </span>
        Next
        <span class="is-sr-only"> page</span>
      </a>
    <% } else { %>
      <a class="pagination-next"
         title="This is the last page"
         aria-label="Last page"
         aria-current="true"
         disabled>Next
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>

    <!-- Last Page. -->
    <% if (page.next != 0) { %>
      <a class="pagination-next"
         href="<%= pagination_url(page.total) %>" 
         aria-label="Go to last page"
         data-rel="last">
        <span class="is-sr-only">Go to </span>
        Last
        <span class="is-sr-only"> page</span>
      </a>
    <% } else { %>
      <a class="pagination-next"
         title="This is the last page"
         aria-label="Last page"
         aria-current="true"
         disabled>Last
        <span class="is-sr-only"> page</span>
      </a>
    <% } %>

    <!-- Main Pagination List -->
    <ul class="pagination-list">

      <!-- Previous Page. -->
      <li class="blog-previous-no-responsive">
      <% if (page.prev_link) { %>
        <a class="pagination-previous"
           href="<%= url_for(page.prev_link) %>" 
           aria-label="Go to previous page"
           rel="prev">&laquo;&nbsp;
           <span class="is-sr-only">Go to previous page</span>
        </a>
      <% } else { %>
        <a class="pagination-previous"
           title="This is the first page"
           aria-label="First page"
           aria-current="true"
           disabled>&laquo;&nbsp;
        <span class="is-sr-only">First page</span>
      </a>
      <% } %>
      </li>

    <% if (page.total > max_links) { %>
      <% if (current - adjacent_links > 1) { %>
      <!-- First Page. -->
      <li class="first">
        <a href="<%= pagination_url(1) %>"
           class="pagination-link" 
           aria-label="Go to page 1">
           <span class="is-sr-only">Go to page </span>1</a>
      </li>
      <% } %>

      <% if (current - adjacent_links > 2) { %>
      <!-- Early (More Pages) Indicator. -->
      <li class="pages-indicator first">
        <span class="pagination-ellipsis">&hellip;</span>
      </li>
      <% } %>
    <% } %>

    <% 
  var cursor; 
  for (cursor = 1; cursor <= page.total; cursor++) { 
    var show_cursor_flag = false;


    if (page.total > max_links) {
      // Complex page numbers.

      if (current <= lower_limit) {
        // Lower limit pages.
        // If the user is on a page which is in the lower limit.
        if (cursor <= max_links) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else if (current >= upper_limit) {
        // Upper limit pages.
        // If the user is on a page which is in the upper limit.
        if (cursor > (page.total - max_links)) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else {
        // Middle pages.
        if ( (cursor >= current - adjacent_links) 
        &&   (cursor <= current + adjacent_links) ) {
          show_cursor_flag = true;
        }
      }
    } else {
      // Simple page numbers.
      show_cursor_flag = true;
    }

    if (show_cursor_flag) { 
      // Calculate Offset Class.
      var page_offset_class = 'pagination--offset-' 
                            + Math.abs(cursor - current);

      // Show Pager.
      %>
      <li class="<%= page_offset_class %>">
        <% if (current != cursor) { %>
        <a href="<%= pagination_url(cursor) %>"
           class="pagination-link" 
           aria-label="Go to page <%= cursor %>">
          <span class="is-sr-only">Go to page </span><%= cursor %>
        </a>
        <% } else { %>
        <a class="pagination-link is-current" 
           aria-label="Page <%= current %>"
           aria-current="true">
          <span class="is-sr-only">Page </span><%= current %>
        </a>
        <% } %>
      </li>
      <% } /* if */ %>
    <% } /* for */ %>

    <% if (page.total > max_links) { %>
      <% if (current + adjacent_links < page.total - 1) { %>
      <!-- Late (More Pages) Indicator. -->
      <li class="pages-indicator last">
        <span class="pagination-ellipsis">&hellip;</span>
      </li>
      <% } %>

      <% if (current + adjacent_links < page.total) { %>
      <!-- Last Page. -->
      <li class="last">
        <a href="<%= pagination_url(page.total) %>"
           class="pagination-link" 
           aria-label="Go to page <%= page.total %>">
           <span class="is-sr-only">Go to page </span><%= page.total %></a>
      </li>
      <% } %>
    <% } %>

      <!-- Next Page. -->
      <li class="blog-next-no-responsive">
      <% if (page.next_link) { %>
        <a class="pagination-next"
           href="<%= url_for(page.next_link) %>" 
           aria-label="Go to next page"
           rel="next">&nbsp;&raquo;
           <span class="is-sr-only">Go to next page</span>
        </a>
      <% } else { %>
        <a class="pagination-next"
           title="This is the last page"
           aria-label="Last page"
           aria-current="true"
           disabled>&nbsp;&raquo;
        <span class="is-sr-only">Last page</span>
      </a>
      <% } %>
      </li>
    </ul>
  <% } %>
</nav>

Now the pagination tutorial is done.

I think this is all for now.

  • .

What is Next ?

Feels like tired, after finishing pagination Tutorial ? This kitten, need some sleep now.. Just like the kitten, you may need some rest too. Our pagination tutorial is finished. But you may continue to explore other challenging topic, tomorrow, or right away.

adorable kitten

Consider continue reading [ Hexo - Pagination - Handling URL ]. There are still, one interesting topic about Pagination in Hexo.

Thank you for reading.