From 53e9a6d196ebd97cef8d745732d836393e4b5bdd Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Thu, 9 Oct 2025 16:30:17 +0800 Subject: [PATCH 1/3] Implement fair randomized ordering for design help providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses #65 This implements client-side randomization of provider display order to ensure all companies receive fair visibility. Each visitor sees a randomized order that remains stable across page reloads. ## Implementation **Provider Template (_includes/design_help/provider.html)**: - Added ID attribute for hash-link support (#chipflow, #mabrains, etc.) - Added provider-item class for JavaScript targeting - Added data-provider-slug attribute for identification **Design Help Page (design-help.html)**: - Wrapped provider list in flexbox container - Added inline JavaScript shuffle script that: - Uses localStorage to persist random seed per user - Implements seeded Linear Congruential Generator (LCG) - Applies Fisher-Yates shuffle algorithm - Sets CSS order property on each provider - Runs inline to prevent FOUC (Flash of Unstyled Content) - Gracefully handles localStorage unavailability ## Features ✅ Randomized order: Different users see different provider sequences ✅ Persistent: Each user sees same order across page reloads ✅ No FOUC: Inline script prevents visual flashing ✅ Hash links: #chipflow etc. still work after reordering ✅ Progressive enhancement: Works without JavaScript (shows default order) ✅ Privacy-friendly: Falls back gracefully when localStorage disabled ## Technical Details - Seeded RNG ensures deterministic randomization - CSS flexbox order property changes visual order without DOM manipulation - Inline script executes before page render - Compatible with static Jekyll site generation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- _includes/design_help/provider.html | 2 +- design-help.html | 78 +++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/_includes/design_help/provider.html b/_includes/design_help/provider.html index eacdd734..37f6ed85 100644 --- a/_includes/design_help/provider.html +++ b/_includes/design_help/provider.html @@ -1,4 +1,4 @@ -
+
{% if include.provider.avatar %}
diff --git a/design-help.html b/design-help.html index cd35e681..68424e6b 100644 --- a/design-help.html +++ b/design-help.html @@ -17,10 +17,80 @@

Professional Services

Please contact them directly to discuss your project needs.

- {% assign professional_help = site.design_help | where: "category", "professional" %} - {% for provider in professional_help %} - {% include design_help/provider.html provider=provider %} - {% endfor %} +
+ {% assign professional_help = site.design_help | where: "category", "professional" %} + {% for provider in professional_help %} + {% include design_help/provider.html provider=provider %} + {% endfor %} +
+ +
From 2fb5daa28d62929da2ff2e0225042c8956bb99c4 Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 10 Oct 2025 11:09:20 +0800 Subject: [PATCH 2/3] Address code review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes identified by GitHub Copilot code review: 1. Define magic numbers as named constants: - Added LCG_MODULUS = 2147483647 (2^31 - 1, Mersenne prime) - Added LCG_MULTIPLIER = 16807 (MINSTD LCG parameters) - Improves code maintainability and clarity 2. Fix order assignment logic: - Changed from: providers[shuffledIndices[i]].style.order = i - Changed to: providers[i].style.order = shuffledIndices[i] - More intuitive: "provider i goes to position shuffledIndices[i]" Both changes improve code readability without affecting functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- design-help.html | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/design-help.html b/design-help.html index 68424e6b..17580e6b 100644 --- a/design-help.html +++ b/design-help.html @@ -28,15 +28,19 @@

Professional Services

(function() { 'use strict'; + // LCG (Linear Congruential Generator) parameters - MINSTD + var LCG_MODULUS = 2147483647; // 2^31 - 1 (Mersenne prime) + var LCG_MULTIPLIER = 16807; + // Seeded Random Number Generator (Linear Congruential Generator) function SeededRandom(seed) { - this.seed = seed % 2147483647; - if (this.seed <= 0) this.seed += 2147483646; + this.seed = seed % LCG_MODULUS; + if (this.seed <= 0) this.seed += LCG_MODULUS - 1; } SeededRandom.prototype.next = function() { - this.seed = (this.seed * 16807) % 2147483647; - return (this.seed - 1) / 2147483646; + this.seed = (this.seed * LCG_MULTIPLIER) % LCG_MODULUS; + return (this.seed - 1) / (LCG_MODULUS - 1); }; // Fisher-Yates shuffle with seeded random @@ -58,14 +62,14 @@

Professional Services

try { seed = localStorage.getItem(SEED_KEY); if (!seed) { - seed = Math.floor(Math.random() * 2147483647); + seed = Math.floor(Math.random() * LCG_MODULUS); localStorage.setItem(SEED_KEY, seed); } else { seed = parseInt(seed, 10); } } catch (e) { // localStorage not available (private mode, etc.) - seed = Math.floor(Math.random() * 2147483647); + seed = Math.floor(Math.random() * LCG_MODULUS); } // Get all provider items @@ -87,7 +91,7 @@

Professional Services

// Apply CSS order to each provider for (var i = 0; i < providers.length; i++) { - providers[shuffledIndices[i]].style.order = i; + providers[i].style.order = shuffledIndices[i]; } })(); From 9831612da3adccaf987686b1c28f6b28338b12aa Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 10 Oct 2025 16:13:28 +1030 Subject: [PATCH 3/3] Address code review feedback - comprehensive fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes all issues identified in code review: **Critical Issues Fixed:** 1. Hash navigation race condition - Added scrollIntoView after shuffle to ensure correct scroll position when users navigate to #chipflow etc. 2. Seed validation - Added validation to check for NaN and valid range, regenerates seed if corrupted localStorage value detected **Important Improvements:** 3. Accessibility - Added sr-only ARIA live region to inform screen reader users that provider order is randomized and may differ from visual order 4. Inline styles - Moved flexbox styles from inline to {% include layouts/nav/nav.html drop_search=false alignment_class="ms-auto" %} {% include layouts/header/page-title.html %} @@ -16,8 +22,11 @@

Professional Services


Note: These services are not affiliated with or endorsed by wafer.space. Please contact them directly to discuss your project needs.

+
+ Provider order is randomized for fairness. Visual order may differ from screen reader order. +
-
+
{% assign professional_help = site.design_help | where: "category", "professional" %} {% for provider in professional_help %} {% include design_help/provider.html provider=provider %} @@ -66,9 +75,17 @@

Professional Services

localStorage.setItem(SEED_KEY, seed); } else { seed = parseInt(seed, 10); + // Validate seed is a valid number in expected range + if (isNaN(seed) || seed <= 0 || seed >= LCG_MODULUS) { + seed = Math.floor(Math.random() * LCG_MODULUS); + localStorage.setItem(SEED_KEY, seed); + } } } catch (e) { // localStorage not available (private mode, etc.) + if (console && console.debug) { + console.debug('Failed to access localStorage for provider shuffle:', e); + } seed = Math.floor(Math.random() * LCG_MODULUS); } @@ -93,6 +110,14 @@

Professional Services

for (var i = 0; i < providers.length; i++) { providers[i].style.order = shuffledIndices[i]; } + + // Re-scroll to hash target if present (fixes race condition) + if (window.location.hash) { + var target = document.getElementById(window.location.hash.substring(1)); + if (target) { + target.scrollIntoView(); + } + } })();