Glossary with Alphabet Navigation

This glossary implementation uses Loops & Logic to create a dynamic A-Z navigation for browsing terminology.

– The template first loops through all glossary posts to check if any entries start with numbers, then builds a complete alphabet list (with “#” added when numeric entries exist).
– Next, it creates a list of actually available letters by examining the first character of each post title, converting it to uppercase, and storing unique values.
– With these lists prepared, the template renders a container with Schema.org markup, a search box with a status indicator, and two navigation options – a horizontal A-Z list for desktop and a dropdown for mobile.
– For each available letter, the template loops through posts again to group entries under the appropriate letter headings, using conditional logic to handle special cases like numeric entries.

The result is a fully responsive glossary where L&L’s logic handles all the filtering and content organization before any JavaScript runs on the page.

Preview

No matching glossary terms found. Please try a different search.

#

4 Reasons your Shortcodes are on the Way Out

A

An absolute beginner's guide to Loops & Logic

C

Creating CCS-like shortcodes in Loops & Logic
Custom Content Shortcode to Loops & Logic Transition - FAQs

E

Everything you need to know about the Loop tag

G

Getting started with L&L terminology and syntax

U

Update: animated pagination, math variable, and more in version 2.4.4
Update: editor upgrade, Pods support, and new export format in versions 3.3.0 to 3.3.1
Update: insert headers/footers, RegEx, and more in versions 3.2.0 to 3.2.9
Update: make your templates more responsive with the new device variable in Loops & Logic version 2.4.3
Update: multiple comparisons in a single If tag, new modulo operator, and more in Loops & Logic Version 2.4.1
Update: new builder integration and improvements in versions 3.1.0 to 3.1.9
Update: new docs, restructured plugins, and more in version 3.0.0
Update: template previews and new code editor in versions 4.0.0 to 4.0.2
Update: Theme locations and more with Loops & Logic Version 1.2.0
Update: Theme PHP Templates in Layouts and more in Loops & Logic Version 1.2.3

W

Why don't you just learn PHP?
<Note>First, check if there are any numeric entries</Note>
<Set has_numeric_entries>false</Set>
<Loop type=post post_type=glossary orderby=title order=asc>
  <Set first_char><Format length=1><Field title /></Format></Set>
  <If check="{Get first_char}" matches_pattern="/[0-9]/">
    <Set has_numeric_entries>true</Set>
  </If>
</Loop>

<Note>Create alphabet list, conditionally including # for numbers</Note>
<Set alphabet>
  <List>
    <If check="{Get has_numeric_entries}" is value="true">
      <Item>#</Item>
    </If>
    <Item>A</Item><Item>B</Item><Item>C</Item><Item>D</Item><Item>E</Item><Item>F</Item>
    <Item>G</Item><Item>H</Item><Item>I</Item><Item>J</Item><Item>K</Item><Item>L</Item>
    <Item>M</Item><Item>N</Item><Item>O</Item><Item>P</Item><Item>Q</Item><Item>R</Item>
    <Item>S</Item><Item>T</Item><Item>U</Item><Item>V</Item><Item>W</Item><Item>X</Item>
    <Item>Y</Item><Item>Z</Item>
  </List>
</Set>

<Note>Build list of available letters</Note>
<Set available_letters>
  <List>
  </List>
</Set>

<Note>Populate available letters list</Note>
<Loop type=post post_type=glossary orderby=title order=asc>
  <Set first_char><Format length=1><Field title /></Format></Set>
  <Set first_letter><Format case=upper><Get first_char /></Format></Set>
  
  <Note>Check if this is a number and use #</Note>
  <If check="{Get first_char}" matches_pattern="/[0-9]/">
    <Set first_letter>#</Set>
  </If>
  
  <Set already_added>false</Set>
  
  <Note>Check if letter already in our list</Note>
  <Loop list="{Get available_letters}">
    <If check="{Field /}" is value="{Get first_letter}">
      <Set already_added>true</Set>
    </If>
  </Loop>
  
  <Note>If not already in list, add it</Note>
  <If check="{Get already_added}" is value="false">
    <Set available_letters>
      <List>
        <Loop list="{Get available_letters}">
          <Item><Field /></Item>
        </Loop>
        <Item><Get first_letter /></Item>
      </List>
    </Set>
  </If>
</Loop>

<Note>Schema.org markup for glossary</Note>
<JSON-LD>
  {
    "@context": "https://schema.org",
    "@type": "DefinedTermSet",
    "name": "Glossary",
    "description": "Complete glossary of terms"
  }
</JSON-LD>

<div class="glossary-container" itemscope itemtype="https://schema.org/DefinedTermSet">
  
  <!-- Search Box -->
  <div class="glossary-search-container">
    <div class="search-field-wrapper">
      <label for="glossary-search" class="sr-only">Search glossary terms</label>
      <input type="text" id="glossary-search" class="glossary-search-input" placeholder="Search glossary terms..." aria-label="Search glossary terms">
      <button type="button" id="glossary-search-btn" class="glossary-search-button">Search</button>
    </div>
    <div id="search-status" class="search-status" aria-live="polite"></div>
  </div>
  
  <!-- Desktop A-Z Navigation -->
  <nav class="alphabet-nav" aria-label="Glossary alphabetical navigation">
    <ul class="alphabet-list">
      <Loop list="{Get alphabet}">
        <Set current_letter><Field /></Set>
        <li>
          <Set is_available>false</Set>
          
          <Note>Check if this letter has entries</Note>
          <Loop list="{Get available_letters}">
            <If check="{Field /}" is value="{Get current_letter}">
              <Set is_available>true</Set>
            </If>
          </Loop>
          
          <If check="{Get is_available}" is value="true">
            <a href="#{Get current_letter}" class="alphabet-link active">
              <Get current_letter />
            </a>
          <Else />
            <span class="alphabet-link disabled" aria-disabled="true">
              <Get current_letter />
            </span>
          </If>
        </li>
      </Loop>
    </ul>
  </nav>
  
  <!-- Mobile Dropdown Navigation -->
  <div class="mobile-alphabet-nav">
    <label for="letter-select" class="sr-only">Jump to letter</label>
    <select id="letter-select" onChange="if(this.value) window.location.hash = this.value;" aria-label="Select a letter to jump to that section">
      <option value="">Jump to Letter</option>
      <Loop list="{Get alphabet}">
        <Set current_letter><Field /></Set>
        <Set is_available>false</Set>
        
        <Note>Check if this letter has entries</Note>
        <Loop list="{Get available_letters}">
          <If check="{Field /}" is value="{Get current_letter}">
            <Set is_available>true</Set>
          </If>
        </Loop>
        
        <If check="{Get is_available}" is value="true">
          <option value="#{Get current_letter}"><Get current_letter /></option>
        <Else />
          <option value="" disabled><Get current_letter /> (No entries)</option>
        </If>
      </Loop>
    </select>
  </div>
  
  <!-- Main glossary content -->
  <div class="glossary-content">
    <!-- No results message -->
    <div id="no-results" class="no-results">
      <p>No matching glossary terms found. Please try a different search.</p>
    </div>
    
    <Note>Loop through available letters and create sections</Note>
    <Loop list="{Get available_letters}">
      <Set current_letter><Field /></Set>
      
      <section id="{Get current_letter}" class="letter-section">
        <h2 class="letter-heading"><Get current_letter /></h2>
        
        <dl class="glossary-terms">
          <Note>Loop through glossary posts for this letter</Note>
          <Loop type=post post_type=glossary orderby=title order=asc>
            <Set first_char><Format length=1><Field title /></Format></Set>
            <Set post_first_letter><Format case=upper><Get first_char /></Format></Set>
            
            <Note>If this is a number and we're in the # section</Note>
            <If check="{Get current_letter}" is value="#">
              <If check="{Get first_char}" matches_pattern="/[0-9]/">
                <dt itemprop="name" id="term-{Field id}" class="glossary-term" data-term="{Field title}"><Field title /></dt>
                <dd itemprop="description" class="glossary-def"><Field glossary_description /></dd>
              </If>
            <Else />
              <Note>Regular letter matching</Note>
              <If check="{Get post_first_letter}" is value="{Get current_letter}">
                <dt itemprop="name" id="term-{Field id}" class="glossary-term" data-term="{Field title}"><Field title /></dt>
                <dd itemprop="description" class="glossary-def"><Field glossary_description /></dd>
              </If>
            </If>
          </Loop>
        </dl>
      </section>
    </Loop>
  </div>
</div>