feat(website): randomise which logo comes first on /logo/ per visit
Some checks failed
CI / lint (push) Failing after 29s
CI / validate-json (push) Successful in 23s
CI / markdown-links (push) Successful in 14s
Deploy site / deploy (push) Successful in 4s
CI / test (push) Successful in 1m29s

Hugo sorts map keys, so `range (dict "m" … "r" …)` always put M first
and gave it the "first thing on the page" vote. The template now ranges
over an ordered list (front-matter `order:` can pin it), and a tiny
inline script on poll pages coin-flips the visual order before first
paint, applied via CSS `order` on every M/R pair and remembered in
sessionStorage so a reload or a vote doesn't shuffle the page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y8kPxizsExc95ZhvfJmaCw
This commit is contained in:
Daniel Maksymilian Syrnicki 2026-08-27 12:43:46 +02:00
parent c3c59b4bea
commit 722dac4843
3 changed files with 25 additions and 12 deletions

View file

@ -559,4 +559,12 @@ main.container {
.home-poll:hover { border-color: var(--accent); }
.home-poll .pm { height: 1.1rem; width: auto; color: var(--accent); }
.home-poll .pm + .pm { color: #af6428; }
/* Random left/right per visit data-logo-order is set in head.html before first paint.
Every pair container is a grid/flex, so `order` swaps the columns without touching the DOM. */
[data-logo-order="rm"] .poll-card[data-choice="m"],
[data-logo-order="rm"] .show-cell.show-cell--m,
[data-logo-order="rm"] .poll-final-card[data-choice="m"],
[data-logo-order="mr"] .poll-card[data-choice="r"],
[data-logo-order="mr"] .show-cell.show-cell--r,
[data-logo-order="mr"] .poll-final-card[data-choice="r"] { order: 1; }
@media (prefers-reduced-motion: reduce) { .poll-bar i, .poll-btn { transition: none; } }

View file

@ -1,7 +1,11 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>document.documentElement.classList.replace('no-js','js');</script>
<title>{{ if .IsHome }}{{ site.Title }} — {{ site.Params.description }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}</title>
{{ if eq .Type "poll" }}{{/* Coin-flip which mark sits left/first, so neither side collects the
"first thing on the page" votes. Decided before first paint (no reflow), applied by CSS `order`,
and kept for the tab session so a reload or a vote doesn't shuffle the page under the visitor. */}}
<script>(function(){var k='furtka-logo-order',o=null;try{o=sessionStorage.getItem(k)}catch(e){}if(o!=='mr'&&o!=='rm'){o=Math.random()<0.5?'mr':'rm';try{sessionStorage.setItem(k,o)}catch(e){}}document.documentElement.setAttribute('data-logo-order',o)})();</script>
{{ end }}<title>{{ if .IsHome }}{{ site.Title }} — {{ site.Params.description }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}</title>
<meta name="description" content="{{ with .Params.description }}{{ . }}{{ else }}{{ site.Params.description }}{{ end }}">
<meta name="theme-color" content="#f7f6f3" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#0d0d0f" media="(prefers-color-scheme: dark)">

View file

@ -1,7 +1,8 @@
{{ define "main" }}
{{- $p := .Params -}}
{{- $M := site.Data.logo.m -}}
{{- $R := site.Data.logo.r -}}
{{/* Display order of the two marks; flip via `order: [r, m]` in front matter so neither
side gets the "first thing on the page" advantage for the whole poll. */}}
{{- $order := $p.order | default (slice "m" "r") -}}
{{- $s := $p.show -}}
<article class="poll" data-poll="{{ $p.poll }}" data-api="/api/poll/{{ $p.poll }}">
<header class="page-header">
@ -12,7 +13,7 @@
{{/* ── Quick vote ─────────────────────────────────────────── */}}
<div class="poll-grid" id="vote">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
{{ $c := index $p $k }}
<section class="poll-card" data-choice="{{ $k }}" aria-labelledby="poll-{{ $k }}-title">
<div class="poll-art poll-art--{{ $k }}">{{ $d.display | safeHTML }}</div>
@ -38,7 +39,7 @@
<h2 class="show-h">{{ $s.sizes }}</h2>
<p class="show-p">{{ $s.sizes_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<div class="sizes">
@ -57,7 +58,7 @@
<h2 class="show-h">{{ $s.name }}</h2>
<p class="show-p">{{ $s.name_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<div class="lock lock-h">{{ $d.lockup_h | safeHTML }}</div>
@ -71,7 +72,7 @@
<h2 class="show-h">{{ $s.browser }}</h2>
<p class="show-p">{{ $s.browser_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<div class="mock">
@ -89,7 +90,7 @@
<h2 class="show-h">{{ $s.phone }}</h2>
<p class="show-p">{{ $s.phone_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<div class="tiles"><img src="/img/logo/{{ $k }}/tile.png" width="96" height="96" alt=""><img src="/img/logo/{{ $k }}/tile-round.png" width="96" height="96" alt=""></div>
@ -102,7 +103,7 @@
<h2 class="show-h">{{ $s.box }}</h2>
<p class="show-p">{{ $s.box_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<pre class="term"><i>{{ $d.banner }}</i>
@ -118,7 +119,7 @@
<h2 class="show-h">{{ $s.shared }}</h2>
<p class="show-p">{{ $s.shared_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<img class="shot" src="/img/logo/{{ $k }}/social.png" width="600" height="315" alt="">
@ -131,7 +132,7 @@
<h2 class="show-h">{{ $s.colour }}</h2>
<p class="show-p">{{ $s.colour_text }}</p>
<div class="show-pair">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="show-cell show-cell--{{ $k }}">
<span class="show-tag">{{ index $p $k "short" }}</span>
<div class="grounds">
@ -150,7 +151,7 @@
<section class="poll-final">
<h2 class="show-h">{{ $s.final }}</h2>
<div class="poll-final-row">
{{ range $k, $d := (dict "m" $M "r" $R) }}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
<div class="poll-final-card" data-choice="{{ $k }}">
<span class="poll-final-mark show-cell--{{ $k }}">{{ $d.flat | safeHTML }}</span>
<button type="button" class="poll-btn" data-vote="{{ $k }}">{{ $p.labels.vote }} — {{ index $p $k "short" }}</button>