Skip to main content

Teaser Category

The code below works in all 3 different layouts. It will categorize teasers based on the text that is added after a "|" 

  • If no "|" character is detected, the teaser will be categorized as "Uncategorized" 
  • Can be the text of the page or the title text of the teaser
  • Accounts for mobile view
  • Uses a highlighted color when hovered over
  • Creates a count for how many teasers are in the category
  • In use on this page
  • Must use in HTML block on a page cant add site-wide (yet?)
Teaser Category Code 

<style>
  /*---------------------------------------*/
  /* 1) TAB BAR – keep yours exactly       */
  /*---------------------------------------*/
  .items .tablist {
    display: flex;
    justify-content: flex-start;
    min-width: 100%;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 1rem;
    margin: 0 0 1rem;
    padding: 0 1rem;
  }
  .items .tablist [role="tab"] {
    margin-bottom: -2px;
    background: #f5f5f5;
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: 4px 4px 0 0;
    padding: 0.5em 1em;
    cursor: pointer;
    transition: background 0.2s;
    outline: none;
    white-space: nowrap;
  }
  .items .tablist [role="tab"]:hover {
    background: #e0e0e0;
  }
  .items .tablist [role="tab"][aria-selected="true"] {
    background: #fff;
    border-color: #ccc;
    font-weight: bold;
  }

  /*---------------------------------------*/
  /* 2) PANELS – flex container            */
  /*---------------------------------------*/
  .items .tabpanels [role="tabpanel"] {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: stretch;
    padding: 1rem;
    box-sizing: border-box;
  }
  .items .tabpanels [role="tabpanel"][hidden] {
    display: none !important;
  }

  /*---------------------------------------*/
  /* 3) CARD BASE – 2‑column default       */
  /*    (subtract only ONE gap: 1rem)      */
  /*---------------------------------------*/
  .items .tabpanels [role="tabpanel"] .poc-instance {
    flex: 0 0 calc((100% - 1rem) / 2);
    max-width: calc((100% - 1rem) / 2);
    box-sizing: border-box;
  }

  /*---------------------------------------*/
  /* 4) OVERRIDES BASED ON COL NUMBERS     */
  /*---------------------------------------*/
  /* 1‑column */
  .poc-layout-one-column .items .tabpanels [role="tabpanel"] .poc-instance {
    flex: 0 0 100% ;
    max-width: 100% ;
  }

  /* 2‑column */
  .poc-layout-three-column .items .tabpanels [role="tabpanel"] .poc-instance {
    flex: 0 0 calc((100% - 1rem) / 2) ;
    max-width: calc((100% - 1rem) / 2) ;
  }

  /* 3‑column  */
  /* subtract TWO gaps: 1rem * (3‑1) = 2rem */
  .poc-layout-three-column .items .tabpanels [role="tabpanel"] .poc-instance {
    flex: 0 0 calc((100% - 2rem) / 3) ;
    max-width: calc((100% - 2rem) / 3) ;
  }

  /*---------------------------------------*/
  /* 5) COLLAPSE TO ONE‑COLUMN ON MOBILE   */
  /*---------------------------------------*/
  @media (max-width: 600px) {
    .items .tabpanels [role="tabpanel"] .poc-instance {
      flex: 0 0 100% !important;
      max-width: 100% !important;
    }
  }

  /*---------------------------------------*/
  /* 6) PHONE‑WRAP TABS (<480px)           */
  /*---------------------------------------*/
  @media (max-width: 480px) {
    .items .tablist {
      flex-wrap: wrap;
      overflow-x: hidden;
    }
    .items .tablist [role="tab"] {
      flex: 0 1 auto;
      margin: 0 0.5rem 0.5rem 0;
      padding: 0.5em 1em;
      white-space: nowrap;
      margin-bottom: -2px;
    }
  }


</style>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const container = document.querySelector('.items');
  if (!container) return;

  // 1️⃣ Grab your original cards
  const originals = Array.from(container.querySelectorAll('article.poc-instance'));
  if (originals.length === 0) return;

  // 2️⃣ Parse category/title from header text
  const groupsMap = {};  // category → array of indexes
  originals.forEach((card, i) => {
    const h3 = card.querySelector('header h3');
    if (!h3) return;

    const txt = h3.textContent.trim();
    let title    = txt,
        category = 'Uncategorized';

    if (txt.includes('|')) {
      [title, category] = txt.split('|').map(s => s.trim());
    }

    // overwrite the displayed title
    h3.innerHTML = `<span>${title}</span>`;

    // collect into groupsMap
    if (!groupsMap[category]) groupsMap[category] = [];
    groupsMap[category].push(i);
  });

  // 3️⃣ Build “All” + other tabs
  const types = ['All', ...Object.keys(groupsMap)];
  groupsMap['All'] = originals.map((_, i) => i);

  // 4️⃣ Clear existing content
  container.innerHTML = '';

  // 5️⃣ Build the ARIA tablist
  const tablist = document.createElement('div');
  tablist.className = 'tablist';
  tablist.setAttribute('role', 'tablist');
  container.appendChild(tablist);

  // 6️⃣ Build panels container
  const panelsContainer = document.createElement('div');
  panelsContainer.className = 'tabpanels';
  container.appendChild(panelsContainer);

  // 7️⃣ Create each tab + panel
  types.forEach((type, idx) => {
    // — Tab button
    const tab = document.createElement('button');
    tab.id = `tab-${idx}`;
    tab.setAttribute('role', 'tab');
    tab.setAttribute('aria-controls', `panel-${idx}`);
    tab.textContent = `${type} (${groupsMap[type].length})`;
    tab.setAttribute('aria-selected', idx === 0 ? 'true' : 'false');
    if (idx !== 0) tab.setAttribute('tabindex', '-1');
    tablist.appendChild(tab);

    // — Panel
    const panel = document.createElement('div');
    panel.id = `panel-${idx}`;
    panel.setAttribute('role', 'tabpanel');
    panel.setAttribute('aria-labelledby', tab.id);
    if (idx !== 0) panel.hidden = true;
    panelsContainer.appendChild(panel);

    // — Clone & append cards for this group
    groupsMap[type].forEach(i => {
      panel.appendChild(originals[i].cloneNode(true));
    });

    // — Click handler
    tab.addEventListener('click', () => activateTab(idx));

    // — Keyboard nav
    tab.addEventListener('keydown', e => {
      let newIdx;
      if (e.key === 'ArrowRight')      newIdx = (idx + 1) % types.length;
      else if (e.key === 'ArrowLeft')  newIdx = (idx - 1 + types.length) % types.length;
      else if (e.key === 'Home')       newIdx = 0;
      else if (e.key === 'End')        newIdx = types.length - 1;
      else return;
      e.preventDefault();
      document.getElementById(`tab-${newIdx}`).focus();
      activateTab(newIdx);
    });
  });

  // 8️⃣ Activation logic
  function activateTab(activeIndex) {
    types.forEach((_, i) => {
      const tab   = document.getElementById(`tab-${i}`);
      const panel = document.getElementById(`panel-${i}`);
      const isActive = i === activeIndex;

      tab.setAttribute('aria-selected', String(isActive));
      if (isActive) tab.removeAttribute('tabindex');
      else          tab.setAttribute('tabindex', '-1');

      panel.hidden = !isActive;
    });
  }

  // 9️⃣ Initialize first tab
  activateTab(0);
});
</script>