Skip to content

Tine Preset Support Pack

Diagnostic and troubleshooting reference for the Tine hardware preset — comparison table, specs-table mode, bulk pricing, and hardware metafields.


FAQ

Q: The comparison table columns are not sorting correctly.

A: The sort extracts the first numeric token from each cell value and compares numbers; values with no numeric token fall back to locale-aware string comparison. In practice:

  • Metric threads sort correctly by number — M8 < M10 < M12 — with no zero-padding needed
  • European decimal commas are normalized ("5,5 mm" sorts as 5.5)
  • Composite codes like A2-70 sort by their first numeric token (2 in this example); if a standards-code column sorts surprisingly, keep its values in one consistent format

Q: The specs table mode is showing unsorted data in the wrong column.

A: In orientation: rows mode, the table renders spec name/value pairs from section blocks in the order they were added in the theme editor. The table is intentionally non-sortable in rows mode (sorting attribute names is not meaningful). To reorder rows, reorder the blocks in the theme editor.

Q: The savings badge is showing but no discount is applied.

A: This is by design — the badge is a messaging display aid only. It does not apply a Shopify discount automatically. To apply actual discounts:

  1. Set up a quantity-break discount in Shopify (Discounts → Create discount → Amount off products → Minimum purchase quantity) or use a price-breaks app.
  2. Set Minimum quantity (bulk_threshold) in the Quick order section to match your first discount tier quantity, and Savings percent (bulk_save_percent) to its percentage.
  3. The badge will show the savings percentage informationally; the actual discount is applied at checkout by Shopify's discount engine.

Q: Quick order section renders no products.

A: The Quick Order section requires a collection to be selected. Check:

  1. In the theme editor, navigate to Quick Order → Section settings. Is a collection selected?
  2. Does the selected collection have at least one published product?
  3. If the collection is empty, the section shows a guided empty state — check your collection rules or populate products.

Q: Cross-reference block is not rendering.

A: The cross-reference block has no block settings. It reads only the uisce_tine.battery_compat product metafield — a /-separated list of compatible systems — and renders one list item per entry. When the metafield is blank, the block renders nothing. Populate battery_compat on the product in Shopify Admin.


Decision Tree: "Why is my comparison table not rendering?"

Symptom: Comparison table section appears but has no columns

  -> Are there blocks added to the comparison table section?
    -> In theme editor: Sections → Comparison table → check for column blocks
    -> No blocks: add at least 2 comparison-column blocks
    -> Yes: Continue

  -> Is orientation set correctly?
    -> 'columns' mode: expects multiple column blocks
    -> 'rows' mode: expects spec-row blocks
    -> Wrong block type for current orientation: switch to the matching block type

  -> Are column headings empty?
    -> Empty column heading will render but look invisible — check each block has a heading

  -> Are the feature rows missing?
    -> Row labels come from the FIRST block's Row labels only — a blank
       Row n label on the first block drops that row for every column
    -> The section reads no metafields: every row comes from the Row
       label/value fields on its blocks
    -> If metafield data is not showing on a PDP, that is the PDP spec
       blocks — check metafield definitions in Shopify Admin

Validator Interpretation

"Lighthouse: Comparison table — interactive element does not have an accessible name"

The comparison table sort buttons are <button> elements inside each <th>. The theme renders every sort button with both visible text (the column title) and an aria-label that names the sort action, so this flag on a stock table usually means a column block was left with an empty Column title — fill the title on that block. The theme ships aria-label="Sort by {column}" — verify this is present in the rendered HTML.

"axe: aria-sort value is not allowed"

The comparison table uses aria-sort="ascending" and aria-sort="descending" per ARIA spec. Valid values are none, ascending, descending, other. If axe flags this, a custom edit may have introduced an invalid value. The sort click handler in niche-tine.js resets every column header to aria-sort="none" and then sets the active <th> to ascending or descending — those are the only values the theme writes.

"Theme check: quick-order section missing collection setting"

This is expected in a fresh install before the merchant selects a collection. Select a collection on the Quick Order section in the theme editor — collection settings cannot take a schema default on Shopify, so a selection is always required. Until one is selected the section renders its guided empty state.


Console Diagnostic Snippet

js
(function tineDiagnostic() {
  'use strict';
  const compTable = document.querySelector('.comparison-table');
  const r = {
    presetBodyClass: document.body.classList.contains('preset-tine'),
    comparisonTable: {
      present: !!compTable,
      mode: compTable ? (compTable.classList.contains('comparison-table--rows') ? 'rows' : 'columns') : 'not found',
      columnHeaders: document.querySelectorAll('.comparison-table__col-header').length,
      valueCells: document.querySelectorAll('.comparison-table__value').length,
      mobileCards: document.querySelectorAll('.comparison-table__card').length,
      sortableHeaders: document.querySelectorAll('th[aria-sort]').length,
      lastAriaSortValue:
        document.querySelector('th[aria-sort]:not([aria-sort="none"])')?.getAttribute('aria-sort') ?? 'none',
    },
    quickOrder: {
      present: !!document.querySelector('.quick-order'),
      rowCount: document.querySelectorAll('.quick-order__row').length,
      bulkPillActive: !!document.querySelector('.quick-order__quantity-break-pill--active'),
    },
    nicheTineMounted: !!document.querySelector('niche-renderer[data-preset="tine"]'),
    presetCSS: !!document.querySelector('link[href*="preset-tine"]'),
  };
  console.group('%cTine Diagnostic Report', 'color:#c0392b;font-weight:bold;font-size:14px');
  console.table(r.comparisonTable);
  console.log('Quick order:', r.quickOrder);
  // niche-tine.js loads via dynamic import — there is no <script> element to probe
  console.log('niche-renderer mounted:', r.nicheTineMounted);
  console.log('Preset CSS:', r.presetCSS);
  console.groupEnd();
  return r;
})();

Accessibility Diagnostic

Comparison table sort accessibility check

js
(function checkComparisonTableA11y() {
  const table = document.querySelector('.comparison-table table');
  if (!table) {
    console.warn('No comparison table found');
    return;
  }
  const sortHeaders = table.querySelectorAll('th[aria-sort]');
  const liveRegion = document.querySelector('[data-comparison-table-announce]');
  console.log({
    tablePresent: true,
    sortableColumns: sortHeaders.length,
    ariaSortValues: [...sortHeaders].map((th) => th.getAttribute('aria-sort')),
    liveRegionPresent: !!liveRegion,
    liveRegionPolite: liveRegion?.getAttribute('aria-live') === 'polite',
    caption: table.querySelector('caption')?.textContent?.trim() ?? 'MISSING',
  });
})();

Expected: sortableColumns > 0 (when sorting is enabled), aria-sort values in none/ascending/descending, liveRegionPresent and liveRegionPolite true. aria-sort sits on the <th> itself (a descendant probe like th [aria-sort] matches nothing), and the announcer is a visually-hidden aria-live="polite" span — it carries no role="status".

Quick order savings badge check

js
const pills = document.querySelectorAll('.quick-order__quantity-break-pill');
const bulkAnnounce = document.querySelector('[data-bulk-announce]');
console.log({
  pillCount: pills.length,
  activePills: [...pills].filter((p) => p.classList.contains('quick-order__quantity-break-pill--active')).length,
  ariaHiddenStates: [...pills].map((p) => p.getAttribute('aria-hidden')),
  liveRegionPresent: !!bulkAnnounce,
  pillText: pills[0]?.textContent?.trim() ?? 'none rendered',
});

Badges only render when bulk_threshold AND bulk_save_percent are both above zero. Visibility toggles via the quick-order__quantity-break-pill--active class alone (not the hidden attribute). aria-hidden stays "true" at every quantity by design — the badge is decorative and the saving is spoken once, through the row's [data-bulk-announce] polite live region, whose text is rendered by Liquid onto data-bulk-announce-text.


Localization Notes

Comparison table locale keys

The sort button's accessible name uses sections.comparison_table.sort_button_aria ("Sort by {column}"). Sort announcements are assembled from sections.comparison_table.announce_sorted_by, announce_direction_ascending, announce_direction_descending, and announce_column_fallback. All 50 locales covered.

Quick order savings badge keys

The badge label uses sections.quick_order.bulk_pill ("Save %"); the screen-reader announcement uses sections.quick_order.announce_bulk_save. Check both are translated correctly for your non-EN markets.

Specs-table PDP block keys

The specs-table-row PDP block strings live under blocks.specs-table-row.* (hyphenated namespace).

Standards names are not localized

Technical standards designations (ASTM F3125, ISO 898-1, DIN EN ISO 7045, BS EN ISO) are English-language designations recognized internationally. They should NOT be translated — use the official designation string verbatim as the spec value. If you need to explain a standard in a customer-facing description, add a richtext block in your product description.


Curl Cheat-Sheet

bash
# Verify Product + additionalProperty JSON-LD on a hardware product
curl -s "https://your-store.myshopify.com/products/tine-cap-screw-m8-50-a2" \
  | grep -A 15 '"additionalProperty"'

# Check the comparison table renders (it ships on the homepage)
curl -s "https://your-store.myshopify.com" \
  | grep -c 'comparison-table'

# Verify preset-tine.css is linked
curl -s "https://your-store.myshopify.com" \
  | grep 'preset-tine'

# Check aria-sort attribute is present on the comparison table (homepage)
curl -s "https://your-store.myshopify.com" \
  | grep 'aria-sort'

Deployment Checklist

Before going live with the Tine preset:

  • [ ] settings.preset set to tine in Theme settings
  • [ ] Comparison table has at least 2 column or 2 row blocks populated
  • [ ] orientation setting is set to the correct mode for each table usage context
  • [ ] Quick order section has a collection selected
  • [ ] bulk_threshold and bulk_save_percent match your actual pricing tier
  • [ ] Product metafield definitions for uisce_tine.* created in Shopify Admin
  • [ ] At least 4 products have material and dimensions_mm metafields populated
  • [ ] Header menu includes product navigation and a link to the quick-order page (page.trade-order template); the comparison table ships on the homepage — no separate compare page exists
  • [ ] JSON-LD validated: Product + additionalProperty appears on product pages
  • [ ] Comparison table aria-sort is verified: run diagnostic snippet above
  • [ ] preset-tine.css linked: confirmed via curl check above

Support pack last updated: 2026-05-03 (Phase 06.10 closeout)

Built for the Shopify Theme Store.