s
  1. Create new page layout layouts/page/filterdemo.html

  2. Create an empty content file.

    hugo new filterdemo.md
  3. Set to use the layout created above in the frontmatter of filterdemo.md:

    layout: filterdemo

Grab hugotagsfilter-...js and put it somewhere in your static directory.

In your filterdemo.html file:

<script src="{{ "vendor/htf/hugotagsfilter-...js" | relURL}}"></script>

Initialize HTF, passing an optional config object. This demo uses the following:

var htfConfig = {
  filters: [
    {
      name: 'section',
      prefix: 'sect-',
      buttonClass: 'sect-button',
      allSelector: '#selectAllSections',
      attrName: 'data-section',
      selectedPrefix: 'ssect-',
      countPrefix: 'csect-'
    },
    {
      name: 'tags',
      prefix: 'tag-',
      buttonClass: 'tag-button',
      allSelector: '#selectAllTags',
      attrName: 'data-tags',
      selectedPrefix: 'stags-',
      countPrefix: 'ctags-'
    },
    {
      name: 'authors',
      prefix: 'auth-',
      buttonClass: 'auth-button',
      allSelector: '#selectAllAuthors',
      attrName: 'data-authors',
      selectedPrefix: 'sauth-',
      countPrefix: 'cauth-'
    }
  ],
  showItemClass: "show-item",
  filterItemClass: "tf-filter-item",
  activeButtonClass: "active",
  counterSelector: "selectedItemCount",
  populateCount: true,
  setDisabledButtonClass: "disable-button"
} 
var htf = new HugoTagsFilter(htfConfig);

You can use as many filter categories as needed.

Tags and section filters are configured by default. If no config object is passed in initialising HTF, the following will be used:

var defaultFilters = [
  {
    name: 'tag',
    prefix: 'tft-',
    buttonClass: 'tft-button',
    allSelector: '#tfSelectAllTags',
    attrName: 'data-tags'
  },
  {
    name: 'section',
    prefix: 'tfs-',
    buttonClass: 'tfs-button',
    allSelector: '#tfSelectAllSections',
    attrName: 'data-section'
  }
]

This demo uses the following styles (in addition to bare.css)

/*** Important ***/
.tf-filter-item {
  display: none;
}

button.active {
  background-color: #ddd;
}

button.disable-button {
  border-style: dashed;
}

.show-item {
  display: inline-block;
  /* or whatever display style is appropriate 
   * for tf-filter-item when it is showing;
   */
}

/*** Aesthetics ***/

.tf-items-container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

.tf-item {
  width: 250px;
  margin: 8px;
  background: #fff;
  padding: 2rem;
  box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .2);
  border-radius: 2px;
  margin-bottom: 2rem
}

pre code {
  overflow-x: auto;
}

$pages and $sections should be fairly straight-forward.

{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}

{{ $sections := .Site.Params.mainSections }}

I don’t have authors configured in my config.toml, only on the front matter of the content, which is why I have to get all authors declared for all content. You may need to do this differently depending on your own setup.

{{ $.Scratch.Set "authors" (slice ) }}
{{ range where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
  {{ with .Params.author }}
    {{ if eq ( printf "%T" . ) "string"  }}
      {{ if ( not ( in ($.Scratch.Get "authors") . ) ) }}
        {{ $.Scratch.Add "authors" . }}
      {{ end }}
    {{ else if ( printf "%T" . ) "[]string" }}
      {{ range . }}
        {{ if ( not ( in ($.Scratch.Get "authors") . ) ) }}
          {{ $.Scratch.Add "authors" . }}
        {{ end }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

Counting and tracking untagged is not required.

{{ $tags := $.Site.Taxonomies.tags.ByCount }}

{{ $.Scratch.Set "untagged" 0 }}
{{ range $pages }}
  {{ with .Params.tags }}{{ else }}{{ $.Scratch.Add "untagged" 1 }}{{ end }}
{{ end }}

For each filter set you want to use, generate buttons (or your preferred element) to use as toggles. For each button:

you also need to add an element inside the button to contain its count (see examples)

  <span id="ssect-{{ . | urlize }}"> -count-</span>

Sections

{
  name: 'section',
  prefix: 'sect-',
  buttonClass: 'sect-button',
  allSelector: '#selectAllSections',
  attrName: 'data-section',
  selectedPrefix: 'ssect-',
  countPrefix: 'csect-'
}

The configuration above corresponds to the button html below:

<h4>Sections</h4>
<button id="selectAllSections" onclick="htf.showAll('section')">
  All Sections
</button>
{{ range $sections }}
  <button class="sect-button" id="sect-{{ . | urlize }}" onclick="htf.checkFilter('{{ . | urlize }}', 'sect-')">
    {{ . | title }} <span id="ssect-{{ . | urlize }}"> -count-</span> | <span id="csect-{{ . | urlize }}"> -count-</span>
  </button>
{{ end }}

Authors

{
  name: 'authors',
  prefix: 'auth-',
  buttonClass: 'auth-button',
  allSelector: '#selectAllAuthors',
  attrName: 'data-authors',
  selectedPrefix: 'sauth-',
  countPrefix: 'cauth-'
}

The configuration above corresponds to the button html below:

<h4>Authors</h4>
<button id="selectAllAuthors" onclick="htf.showAll('authors')">
  All Authors
</button>
{{ range $.Scratch.Get "authors" }}
  <button class="auth-button" id="auth-{{ . | replaceRE "[.]" "_" | urlize }}" onclick="htf.checkFilter('{{ . | replaceRE "[.]" "_" | urlize }}', 'auth-')">
    {{ . }} <span id="sauth-{{ . | replaceRE "[.]" "_" | urlize }}"> -count-</span> | <span id="cauth-{{ . | replaceRE "[.]" "_" | urlize }}"> -count-</span>
    
  </button>
{{ end }}
{{ if gt ( $.Scratch.Get "noAuthors") 0 }}
<button class="auth-button" id="auth-no-author" onclick="htf.checkFilter('no-author', 'auth-')">
  No Author <span id="sauth-no-author"> -count-</span> | <span id="cauth-no-author"> -count-</span>
</button>
{{ end }}

Tags

{
  name: 'tags',
  prefix: 'tag-',
  buttonClass: 'tag-button',
  allSelector: '#selectAllTags',
  attrName: 'data-tags',
  selectedPrefix: 'stags-',
  countPrefix: 'ctags-'
}

The configuration above corresponds to the button html below:

<h4>Tags</h4>
<button class="" id="selectAllTags" onclick="htf.showAll('tags')">
  All Tags
</button>
{{ range $tags }}
  {{ if .Term }}
    <button class="tag-button" id="tag-{{ .Term | replaceRE "[.]" "_" | urlize }}" onclick="htf.checkFilter('{{ .Term | replaceRE "[.]" "_" | urlize }}', 'tag-')">
      <span>{{.Term | humanize | title }}</span>
      <span id="stags-{{ .Term | urlize }}"> -count-</span> | <span id="ctags-{{ .Term | urlize }}"> -count-</span>
    </button>
  {{ end }}
{{ end }}
{{ if gt ( $.Scratch.Get "untagged") 0 }}
<button class="tag-button" id="tag-tfuntagged" onclick="htf.checkFilter('tfuntagged', 'tag-')">
  Untagged <span id="stags-tfuntagged"> -count-</span> | <span id="ctags-tfuntagged"> -count-</span>
</button>
{{ end }}

<div><h2><span id="selectedItemCount"></span> Items</h2></div>
<div class="tf-items-container">      
{{ range $pages.ByPublishDate.Reverse }}
  <div  class="tf-filter-item tf-item" 
        data-tags="{{ with .Params.tags }}{{ range . }}{{ . | replaceRE "[.]" "_" | urlize }} {{ end }}{{ else }} tfuntagged{{ end }}"
        data-section="{{ .Section }}"
        
        data-authors="{{ with .Params.author }}{{ if eq ( printf "%T" . ) "string" }}{{ . | replaceRE "[.]" "_" | urlize }}{{ else if eq ( printf "%T" . ) "[]string" }}{{ range . }}{{ . | replaceRE "[.]" "_" | urlize }} {{end}}{{end}}{{else}}no-author{{end}}"
        
        >
    <h6>{{.Section}}</h6>
    <h5>{{ with .Params.author }}{{ if eq ( printf "%T" . ) "string" }}{{.|humanize|title}}{{ else if eq ( printf "%T" . ) "[]string" }}{{ range . }}{{.|humanize|title}} {{end}}{{end}}{{else}}No Author{{end}}</h5>
    
    <h4><a href="{{ .RelPermalink }}">{{ .Title }}</a></h4>
    
    <div>{{ with .Params.tags }}{{ range . }}<tag>{{ . | humanize | title }}</tag> {{ end }}{{ else }}<tag>untagged</tag>{{ end }}</div>
    
  </div>
{{ end }}
</div>

Items

lorem
No Author

Three

untagged
lorem
No Author

Two

untagged
lorem
No Author

One

untagged
looking-glass
Lewis Carroll

CHAPTER XII. Which Dreamed it?

Chapter Cat
looking-glass
Lewis Carroll

CHAPTER XI. Waking

Chapter
looking-glass
Lewis Carroll

CHAPTER X. Shaking

Chapter Queen Red Queen
looking-glass
Lewis Carroll

CHAPTER IX. Queen Alice

Chapter Queen Red Queen White Queen
looking-glass
Lewis Carroll

CHAPTER VIII. ‘It’s my own Invention’

Chapter
looking-glass
Lewis Carroll

CHAPTER VII. The Lion and the Unicorn

Chapter King Lion Unicorn
looking-glass
Lewis Carroll

CHAPTER VI. Humpty Dumpty

Chapter Egg Humpty Dumpty
looking-glass
Lewis Carroll

CHAPTER V. Wool and Water

Chapter Queen White Queen
looking-glass
Lewis Carroll

CHAPTER IV. Tweedledum And Tweedledee

Chapter Tweedledee Tweedledum
looking-glass
Lewis Carroll

CHAPTER III. Looking-Glass Insects

Chapter Insects Beetle Goat
looking-glass
Lewis Carroll

CHAPTER II. The Garden of Live Flowers

Chapter Garden Queen Red Queen
looking-glass
Lewis Carroll

CHAPTER I. Looking-Glass house

Chapter Cat Jabberwocky
grimm
The Brothers Grimm

Briar Rose

untagged
grimm
The Brothers Grimm

The Straw the Coal and the Bean

untagged
grimm
The Brothers Grimm

Old Sultan

untagged
grimm
The Brothers Grimm

The Travelling Musicians

untagged
grimm
The Brothers Grimm

Jorinda and Jorindel

untagged
grimm
The Brothers Grimm

Hans in Luck

untagged
grimm
The Brothers Grimm

The Golden Bird

untagged
post
No Author

Hugo Tags Filter

untagged
post
No Author

Hello World aka A Bunch Of Lorem

untagged
post
No Author

Using Material Components for the Web

untagged
wonderland
Lewis Carroll

CHAPTER XII. Alice’s Evidence

Chapter Queen of Hearts Rabbit
wonderland
Lewis Carroll

CHAPTER XI. Who Stole the Tarts?

Chapter
wonderland
No Author

Nature

Hugo
wonderland
Lewis Carroll

CHAPTER X. The Lobster Quadrille

Chapter Mock Turtle
wonderland
No Author

Book

untagged
wonderland
Mr. Turtle

CHAPTER IX. The Mock Turtle’s Story

Chapter Duchess Mock Turtle
wonderland
Lewis Carroll

CHAPTER VIII. The Queen’s Croquet-Ground

Chapter Roses Queen of Hearts Cheshire Cat
wonderland
Lewis Carroll

CHAPTER VII. A Mad Tea-Party

Chapter
wonderland
Lewis Carroll

CHAPTER VI. Pig and Pepper

Chapter
wonderland
Lewis Carroll

CHAPTER V. Advice from a Caterpillar

Chapter Caterpillar
wonderland
Lewis Carroll

CHAPTER IV. The Rabbit Sends in a Little Bill

Chapter Rabbit Cake
wonderland
Lewis Carroll

CHAPTER III. A Caucus-Race and a Long Tale

Chapter Birds Mouse
wonderland
Lewis Carroll

CHAPTER II. The Pool of Tears

Chapter Rabbit Mouse
wonderland
Lewis Carroll

CHAPTER I. Down the Rabbit-Hole

Chapter Rabbit Cake