Skip to content

Product Matrix Table

This is the baseline component for product matrices and product comparison tables.

Vad täcker försäkringen? Basic Extra
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Edit this section

Usage

You can have up to 6 products in the product matrix.

As comparison

Vad täcker försäkringen? Basic Extra
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.

As a product chooser

Remember, you need to use input[type=radio] in the header. Look at the implementation section below.

Vad täcker försäkringen? Basic Extra
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Edit this section

States

Selected

  1. Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  2. Vad täcker försäkringen? Basic Extra
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  3. Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  4. Vad täcker försäkringen? Basic Extra
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  5. Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  1. Expanded product row
  2. Selected product
  3. Focused row
  4. Focused column
  5. Hovered row
Edit this section

Anatomy

  1. Vad täcker försäkringen? Basic Extra
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  2. Vad täcker försäkringen? Basic Extra
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
    Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
  1. Product comparison
    1. Caption
    2. Selected product
    3. Product name
    4. Included
    5. Hovered row
    6. Focused row
    7. Expanded row
    8. Expanded content
  2. Product selector
    1. Focused column
    2. Selected product
    3. Spacer
Edit this section

Responsive

Mobile
Tablet portrait
Tablet landscape
Edit this section

Specs

Product-matrix-table
Vad täcker försäkringen? Basic Extra Stor
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Edit this section

Implementation

Product comparison

<div class="if block">
  <div class="if container">
    <table
      class="if table product"
      data-if-selected="1"
      aria-label="Product comparison of our home insurance"
      aria-describedby="pm-implementation-1"
    >
      <thead class="if">
        <tr class="if">
          <th scope="col" class="if"><span class="if" id="pm-implementation-1">Vad täcker försäkringen?</span></th>
          <th scope="col" class="if">
            <button type="button" class="if product-selector previous">
              <span class="if axe sr-only">Previous</span>
            </button>
            <span class="if product-name"> Basic</span>
            <span class="if product-indicator">
              <span class="if"></span>
              <span class="if"></span>
              <span class="if"></span>
            </span>
            <button type="button" class="if product-selector next">
              <span class="if axe sr-only">Next</span>
            </button>
          </th>
          <th scope="col" class="if spacer"></th>
          <th scope="col" class="if">
            <button type="button" class="if product-selector previous">
              <span class="if axe sr-only">Previous</span>
            </button>
            <span class="if product-name"> Extra</span>
            <span class="if product-indicator">
              <span class="if"></span>
              <span class="if"></span>
              <span class="if"></span>
            </span>
            <button type="button" class="if product-selector next">
              <span class="if axe sr-only">Next</span>
            </button>
          </th>
        </tr>
      </thead>
      <tbody class="if">
        <tr class="if expandable">
          <td scope="row" class="if" tabindex="0">Ansvars- och rättskydd</td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if included">
            <span class="if axe sr-only">This is included in the product: Extra</span>
          </td>
        </tr>
        <tr class="if">
          <td class="if">
            Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
            vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
            högre belopp.
          </td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if included"></td>
        </tr>
        <tr class="if expandable">
          <td scope="row" class="if" tabindex="0">Brand och explosion</td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if"></td>
        </tr>
        <tr class="if">
          <td class="if">
            Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
            vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
            högre belopp.
          </td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if"></td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Product chooser

<form class="if">
  <div class="if block">
    <div class="if container">
      <table
        class="if table product selectable"
        data-if-selected="1"
        aria-label="Product comparison of our home insurance"
        aria-describedby="pm-implementation-2"
      >
        <thead class="if">
          <tr class="if">
            <th scope="col" class="if"><span class="if" id="pm-implementation-2">Vad täcker försäkringen?</span></th>
            <th scope="col" class="if">
              <button type="button" class="if product-selector previous">
                <span class="if axe sr-only">Previous</span>
              </button>
              <input
                type="radio"
                tabindex="-1"
                id="product-matrix-selectable-group-1-1"
                class="if checkbox standalone"
                name="product-matrix-selectable-group-1"
              />
              <label tabindex="-1" for="product-matrix-selectable-group-1-1"></label>
              <span class="if product-name"> Basic</span>

              <span class="if product-indicator">
                <span class="if"></span>
                <span class="if"></span>
                <span class="if"></span>
              </span>
              <button type="button" class="if product-selector next">
                <span class="if axe sr-only">Next</span>
              </button>
            </th>
            <th scope="col" class="if spacer"></th>
            <th scope="col" class="if">
              <button type="button" class="if product-selector previous">
                <span class="if axe sr-only">Previous</span>
              </button>
              <input
                type="radio"
                tabindex="-1"
                id="product-matrix-selectable-group-1-2"
                class="if checkbox standalone"
                name="product-matrix-selectable-group-1"
              />
              <label tabindex="-1" for="product-matrix-selectable-group-1-2"></label>
              <span class="if product-name"> Extra</span>

              <span class="if product-indicator">
                <span class="if"></span>
                <span class="if"></span>
                <span class="if"></span>
              </span>
              <button type="button" class="if product-selector next">
                <span class="if axe sr-only">Next</span>
              </button>
            </th>
          </tr>
        </thead>
        <tbody class="if">
          <tr class="if expandable">
            <td scope="row" class="if" tabindex="0">Ansvars- och rättskydd</td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if included">
              <span class="if axe sr-only">This is included in the product: Extra</span>
            </td>
          </tr>
          <tr class="if">
            <td class="if">
              Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
              vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
              högre belopp.
            </td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if included"></td>
          </tr>
          <tr class="if expandable">
            <td scope="row" class="if" tabindex="0">Brand och explosion</td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if"></td>
          </tr>
          <tr class="if">
            <td class="if">
              Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
              vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
              högre belopp.
            </td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if"></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</form>

JavaScript implementation example

Here follows an implementation example of the JavaScript required to make the table accessible and interactive.

Do NOT use this code directly. YMMV.
const minMedium = '60rem';
const mediumMQ = window.matchMedia(`screen and (min-width: ${minMedium})`);

const setColumnAsSelectedByElement = el => {
  let nodeIndex = getNodeIndex(el);
  let normalizedIndex = nodeIndex + 1;
  let productIndex = normalizedIndex / 2;
  setColumnAsSelectedByProductIndex(el, productIndex);
};

const setColumnAsSelectedByProductIndex = (el, productIndex) => {
  el.closest('.if.table.product').setAttribute('data-if-selected', productIndex);
  const selectable = el.closest('.if.table.product.selectable');

  if (selectable) {
    selectable.querySelector(`* > tr > th:nth-child(${productIndex * 2}) [type=radio]`).checked = true;
  }
};

const setColumnAsFocused = el => {
  let nodeIndex = getNodeIndex(el);
  let normalizedIndex = nodeIndex + 1;
  let productIndex = normalizedIndex / 2;
  el.closest('.if.table.product.selectable').setAttribute('data-if-focused', productIndex);
};
const clearColumnFocus = el => {
  el.closest('.if.table.product.selectable').removeAttribute('data-if-focused');
};

const selectPreviousProduct = prev => {
  const products = Array.prototype.map.call(
    prev.closest('thead').querySelectorAll('tr > th:nth-child(even)'),
    th => th
  );

  if (products.indexOf(prev) === 0) {
    setColumnAsSelectedByElement(products[products.length - 1]);
  } else {
    setColumnAsSelectedByElement(products[products.indexOf(prev) - 1]);
  }
};

const selectNextProduct = next => {
  const products = Array.prototype.map.call(
    next.closest('thead').querySelectorAll('tr > th:nth-child(even)'),
    th => th
  );

  if (products.indexOf(next) === products.length - 1) {
    setColumnAsSelectedByElement(products[0]);
  } else {
    setColumnAsSelectedByElement(products[products.indexOf(next) + 1]);
  }
};

const handleMedium = mql => {
  if (mql.matches) {
    document.querySelectorAll('.if.product-selector.previous').forEach(prev => {
      prev.removeEventListener('click', e => {
        selectPreviousProduct(prev.closest('th'));
      });
    });
    document.querySelectorAll('.if.product-selector.next').forEach(next => {
      next.removeEventListener('click', e => {
        selectNextProduct(next.closest('th'));
      });
    });
    document
      .querySelectorAll(
        '.if.table.product.selectable > * > tr > th:nth-child(even),.if.table.product.selectable > * > tr > td:nth-child(even)'
      )
      .forEach(el => {
        el.addEventListener('click', e => {
          setColumnAsSelectedByElement(el);
        });
        el.addEventListener('keydown', e => {
          if (e.key === 'Enter') {
            setColumnAsSelectedByElement(el);
          }
        });
        el.addEventListener('focus', e => {
          setColumnAsFocused(el);
        });
        el.addEventListener('blur', e => {
          clearColumnFocus(el);
        });
      });
    document.querySelectorAll('.if.table.product.selectable > * > tr > th:nth-child(even)').forEach(el => {
      el.setAttribute('tabindex', '0');
    });
  } else {
    document.querySelectorAll('.if.product-selector.previous').forEach(prev => {
      prev.addEventListener('click', e => {
        selectPreviousProduct(prev.closest('th'));
      });
    });
    document.querySelectorAll('.if.product-selector.next').forEach(next => {
      next.addEventListener('click', e => {
        selectNextProduct(next.closest('th'));
      });
    });
    document
      .querySelectorAll(
        '.if.table.product.selectable > * > tr > th:nth-child(even),.if.table.product.selectable > * > tr > td:nth-child(even)'
      )
      .forEach(el => {
        el.removeEventListener('click', e => {
          setColumnAsSelectedByElement(el);
        });
        el.removeEventListener('keydown', e => {
          if (e.key === 'Enter') {
            setColumnAsSelectedByElement(el);
          }
        });
        el.removeEventListener('focus', e => {
          setColumnAsFocused(el);
        });
        el.removeEventListener('blur', e => {
          clearColumnFocus(el);
        });
      });
    document.querySelectorAll('.if.table.product.selectable > * > tr > th:nth-child(even)').forEach(el => {
      el.setAttribute('tabindex', '-1');
    });
  }
};
mediumMQ.addListener(handleMedium);

handleMedium(mediumMQ);
const getNodeIndex = elm => [...elm.parentNode.children].indexOf(elm);

document.querySelectorAll('.if.table.product tr.expandable > td:first-child').forEach(td => {
  td.addEventListener('keydown', e => {
    if (e.key === 'Enter') {
      if (td.parentElement.classList.contains('is-expanded')) {
        td.parentElement.classList.remove('is-expanded');
      } else {
        document.querySelectorAll('.if.table.product tr.expandable.is-expanded').forEach(tr => {
          tr.classList.remove('is-expanded');
        });
        td.parentElement.classList.add('is-expanded');
      }
    }
  });
  td.addEventListener('click', e => {
    if (td.parentElement.classList.contains('is-expanded')) {
      td.parentElement.classList.remove('is-expanded');
    } else {
      document.querySelectorAll('.if.table.product tr.expandable.is-expanded').forEach(tr => {
        tr.classList.remove('is-expanded');
      });
      td.parentElement.classList.add('is-expanded');
    }
  });
});
Edit this section

Contact us