megclaypool
3/24/2020 - 4:43 PM

Pagination Flexbox Hack for Small Screens

[Pagination Flexbox Hack for Small Screens]

Since I control the horizontal and the vertical (err -- wrong show! But I control the twig template, which is almost as good!) I can set up a couple of hacks to make pagination look better on small screens:

  1. Set up 2 spans for the previous item*. One for larger screens that reads, "Previous" and one for smaller screens that reads, "Prev" -- hide and show them at the appropriate widths using styling. Also use area-labelledby to set the "Previous" span to officially label the link for screen readers, regardless of what's currently visible.
  2. Insert a couple of dummy elements whose purpose is to force flexbox to wrap so that prev and next are alone at the top and bottom, with the numbered page links sandwiched between them.
  • Well, actually 4 spans -- 2 spans inside the link for when you're not already viewing the first page, and 2 spans not inside a link, for when you're viewing the first page and so the previous "link" isn't actually a clickable link.
{% set base = 'pager' %}

{% if pagination.items|length > 1 %}
  <nav {{ bem('pager') }} aria-labelledby="pagination-heading">
    <h4 id="pagination-heading" class="visually-hidden">{{ 'Pagination'|t }}</h4>

    <ul {{ bem('list', [], base) }}>
      
      {# Previous #}
      <li {{ bem('item', ['prev'], base) }}>
        {% if pagination.current != 1 %}
          <a href="{{ pagination.prev.href }}" aria-labeledby="previous-item-label"><span class='previous-item-abbreviated'>{{ "Prev"|trans }}</span><span id='previous-item-label' class='previous-item-long'>{{ "Previous"|trans }}</span></a>
        {% else %}
          <span {{ bem('inactive', [], base, ['previous-item-abbreviated']) }}>{{ "Prev"|trans }}</span>
          <span {{ bem('inactive', [], base, ['previous-item-long']) }}>{{ "Previous"|trans }}</span>
        {% endif %}
      </li>

      {# Hack to break pagination nicely on mobile #}
      <li {{ bem('item', ['break-hack'], base) }} aria-hidden="true"></li>

      {# Numbered pages #}
      {% for item_key, item_value in pagination.items %}
        <li {{ bem('item', item_key == pagination.current ? ['active'] : null, base ) }}>
          {% if pagination.current == item_key %}
            {% set title = 'Current page'|t %}
          {% else %}
            {% set title = 'Go to page @key'|t({'@key': item_key}) %}
          {% endif %}

          {% if item_key == pagination.current %}
            <span {{ bem('number', null, base) }} title="{{ title }}" aria-label="{{ title }}">{{ item_key }}</span>
          {% else %}
            <a {{ bem('number', null, pager) }} href="{{ item_value.href }}" title="{{ title }}" aria-label="{{ title }}">
              {{ item_key }}
            </a>
          {% endif %}
        </li>
      {% endfor %}

      {# Hack to break pagination nicely on mobile #}
      <li {{ bem('item', ['break-hack'], base) }} aria-hidden="true"></li>

      {# Next #}
      <li {{ bem('item', ['next'], base) }}>
        {% if pagination.current != pagination.items|length %}
          <a href="{{ pagination.next.href }}" title="{{ "Next Page"|t }}" aria-label="{{ "Next Page"|t }}">{{ "Next"|trans }}</a>
        {% else %}
          <span {{ bem('inactive', [], base) }}>{{ "Next"|trans }}</span>
        {% endif %}
      </li>
    </ul>
  </nav>
{% endif %}
@import "../../variables";

$pager-breakpoint: $breakpoint-sm;

.pager {
  @include rem('margin-bottom', 30px);
}

.pager__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  list-style: none;
  padding: 0;
}

.pager__item {
  align-items: center;
  border-radius: 50%;
  display: inline-flex;
  @include rem('font-size', 14px);
  @include rem('height', 30px);
  justify-content: center;
  @include rem('letter-spacing', 1.7px);
  @include rem('margin', 0 4.5px);
  @include rem('width', 30px);

  @include breakpoint($pager-breakpoint) {
    margin-bottom: 0;
  }

  @include breakpoint($breakpoint-md) {
    font-size: inherit;
  }

  a {
    color: $color--blue-darkish;
  }
}

.pager__item--ellipsis {
  display: none;
}

.pager__item--break-hack {
  background: none;
  border: none;
  flex-basis: 100%;
  height: 0;
  margin: 0;
  padding: 0;
  visibility: hidden;
  width: auto;

  @include breakpoint($pager-breakpoint) {
    display: none;
  }
}

.pager__item--prev,
.pager__item--next {
  width: auto;

  // a {
  //   color: $color--blue;
  // }
}

.pager__item--prev {

  span.previous-item-long {
    display: none;
  }

  @include breakpoint($breakpoint-md) {
    @include rem('margin-right', 50px);

    span.previous-item-abbreviated {
      display: none;
    }

    span.previous-item-long {
      display: inline;
    }
  }
}

.pager__item--next {
  @include breakpoint($breakpoint-md) {
    @include rem('margin-left', 50px);
  }
}

.pager__item--active,
.pagination__item--active {
  background-color: lightgray;
  font-weight: normal;
}

This is the result of {{ dump(pagination) }} in my WordPress twig template:

/Users/noxlady/Sites/techequity/wp-content/plugins/timber-library/vendor/twig/twig/src/Extension/DebugExtension.php:70:
object(Timber\Pagination)[3320]
  public 'current' => int 2
  public 'total' => float 4
  public 'pages' => 
    array (size=4)
      0 => 
        array (size=5)
          'class' => string 'page-number page-numbers' (length=24)
          'link' => string '//localhost:3002/about/our-partners/' (length=42)
          'title' => string '1' (length=1)
          'name' => string '1' (length=1)
          'current' => boolean false
      1 => 
        array (size=5)
          'class' => string 'page-number page-numbers current' (length=32)
          'title' => string '2' (length=1)
          'text' => string '2' (length=1)
          'name' => string '2' (length=1)
          'current' => boolean true
      2 => 
        array (size=5)
          'class' => string 'page-number page-numbers' (length=24)
          'link' => string '//localhost:3002/about/our-partners/page/3/' (length=49)
          'title' => string '3' (length=1)
          'name' => string '3' (length=1)
          'current' => boolean false
      3 => 
        array (size=5)
          'class' => string 'page-number page-numbers' (length=24)
          'link' => string '//localhost:3002/about/our-partners/page/4/' (length=49)
          'title' => string '4' (length=1)
          'name' => string '4' (length=1)
          'current' => boolean false
  public 'next' => 
    array (size=2)
      'link' => string '//localhost:3002/about/our-partners/page/3/' (length=49)
      'class' => string 'page-numbers next' (length=17)
  public 'prev' => 
    array (size=2)
      'link' => string '//localhost:3002/about/our-partners/' (length=42)
      'class' => string 'page-numbers prev' (length=17)
{% set base = 'pager' %}

{% if pagination.pages|length > 1 %}
	<nav {{ bem('') }} aria-label="{{ 'Pagination'|trans }}">
		<ul
			{{ bem('list', [], base) }}>

			{# Previous #}
			<li {{ bem('item', pagination.current == 1 ? ['prev', 'inactive'] : ['prev'], base) }}>
				{% if pagination.current != 1 %}
					<a href="{{ pagination.prev.link }}" aria-labeledby="previous-item-label" rel="next">
						<span class='previous-item-abbreviated'>{{ "Prev"|trans }}</span>
						<span id='previous-item-label' class='previous-item-long'>{{ "Previous"|trans }}</span>
					</a>
				{% else %}
					<span {{ bem('inactive', [], base, ['previous-item-abbreviated']) }}>{{ "Prev"|trans }}</span>
					<span {{ bem('inactive', [], base, ['previous-item-long']) }}>{{ "Previous"|trans }}</span>
				{% endif %}
			</li>

			{# Hack to break pagination nicely on mobile #}
			<li {{ bem('item', ['break-hack'], base) }} aria-hidden="true"></li>

			{# Numbered pages #}
			{% for item in pagination.pages %}
				<li {{ bem('item', loop.index == pagination.current ? ['active'] : null, base ) }}>

					{% if pagination.current == loop.index %}
						{% set title = __('Current page') %}
					{% else %}
						{% set title = __('Go to page %page_number%'|replace({'%page_number%': loop.index})) %}
					{% endif %}

					{% if loop.index == pagination.current %}
						<span {{ bem('number', null, base) }} title="{{ title }}" aria-label="{{ title }}">{{ loop.index }}</span>
					{% else %}
						<a {{ bem('number', null, base) }} href="{{ item.link }}" title="{{ title }}" aria-label="{{ title }}">{{ item.title }}</a>
					{% endif %}
				</li>
			{% endfor %}

			{# Hack to break pagination nicely on mobile #}
			<li {{ bem('item', ['break-hack'], base) }} aria-hidden="true"></li>

			{# Next #}
			<li {{ bem('item', pagination.current == pagination.pages|length ? ['next', 'inactive'] : ['next'], base) }}>
				{% if pagination.current != pagination.pages|length %}
					<a href="{{ pagination.next.link }}" aria-labeledby="next-item-label" rel="next">
						<span id='next-item-label' class='next-item'>{{ __('Next') }}</span>
					</a>
				{% else %}
					<span {{ bem('inactive', [], base, ['next-item']) }}>{{ __('Next') }}</span>
				{% endif %}
			</li>

		</ul>
	</nav>
{% endif %}