From 24526721c18652d04f0fccef8c2324c8504a5dfa Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 07:44:54 +0000 Subject: [PATCH] Fix: the reading-font button was the dimmest control on the page js/dyslexia-font.js sets `opacity: var(--dys-idle-opacity, .72)` on #dys-font-btn. That composites whatever ink the surrounding bar uses down by 28%. Measured on pinpointventures, where the bar's ink is #4b5563 at 6.41:1, the idle button came out #7d858f: 3.73:1, under AA. The comment above that rule argues, at length and correctly, that leaving the site's own ink alone is the one version that cannot go wrong, because it is the ink the site already chose for that bar. Then it takes 28% off it, which undoes exactly that guarantee. A control that exists so people who find text hard to read can read it should not be the hardest thing on the page to read. The default is 1 now. The idle/on distinction already rode on the border and the accent wash, plus OpenDyslexic in the label and aria-pressed for anyone who cannot see either, so nothing is lost by not dimming it. This file is copied into 18 repositories. Seventeen were byte-identical and one, JanVayu's, was ahead: it carries a 44px minimum touch target (WCAG 2.5.5 Target Size) and a placement option that the other seventeen never received. Both improvements are merged, and all 18 copies now hold the same file. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01MrvR2NXsJFVRCeJZFCPuNL --- public/js/dyslexia-font.js | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/public/js/dyslexia-font.js b/public/js/dyslexia-font.js index b312619..7cb8a8a 100644 --- a/public/js/dyslexia-font.js +++ b/public/js/dyslexia-font.js @@ -36,6 +36,7 @@ * --dys-idle-border default currentColor * --dys-idle-opacity default .72 * --dys-font label typeface (default system-ui) + * --dys-min-size minimum touch target (default 44px) * --dys-surface/--dys-fg ground and ink for the fixed corner variant */ (function () { @@ -119,14 +120,28 @@ // fighting this rule on specificity. Point --dys-accent at the site's own // accent token and dark mode follows for free, because a var() is resolved // where it is used, not where it is declared. + // 44px, not 32. This button is a reading aid, so the people most likely + // to reach for it are the ones a small target costs most; WCAG 2.2's + // Target Size (Minimum), 2.5.8, puts the floor at 24px and the older + // 2.5.5 at 44. It was 36x32 on every site using this script. Override + // with --dys-min-size if a host genuinely needs it smaller. + '#dys-font-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;' - + 'min-width:36px;height:32px;padding:0 10px;cursor:pointer;' + + 'min-width:var(--dys-min-size,44px);min-height:var(--dys-min-size,44px);padding:0 12px;cursor:pointer;' + 'border-radius:var(--dys-radius,8px);' + 'border:var(--dys-border-width,1px) solid var(--dys-idle-border,currentColor);' + 'background:transparent;color:inherit;' + 'font:700 13px/1 var(--dys-font,system-ui,sans-serif);' - + 'letter-spacing:.02em;opacity:var(--dys-idle-opacity,.72)}' - + '#dys-font-btn:hover,#dys-font-btn:focus-visible{opacity:1}' + // Idle opacity defaults to 1, not .72. The paragraph below argues that + // leaving the ink alone is the one version that cannot go wrong, because + // it is the ink the site already chose for that bar -- and then took 28% + // off it, which undoes exactly that guarantee. Measured on + // pinpointventures, --color-text #4b5563 is 6.41:1 on the header and the + // composite at .72 is #7d858f, 3.73:1. A control that exists so people who + // find text hard to read can read it should not be the dimmest thing on + // the page. The idle/on distinction rides on the border and the wash, which + // it already did. --dys-idle-opacity is still there for a site whose bar + // has the headroom to spend. + + 'letter-spacing:.02em;opacity:var(--dys-idle-opacity,1)}' // "On" is the site's accent as outline and a wash of itself, with the // label left at whatever ink the bar around it already uses. Two earlier // shapes were worse. A filled button needs an ink that clears 4.5:1 @@ -172,6 +187,18 @@ return el; } + // Where in the slot the button lands. Default is the end of the row, which is + // where it has always gone. A site that wants it somewhere else names an + // element to sit in front of: data-dyslexia-before="#docsLink" on the slot. + // Opt-in on purpose -- this file is shared across nine pages and a change to + // the default would move the button on all of them. + function place(h, b) { + var sel = h.getAttribute && h.getAttribute('data-dyslexia-before'); + var ref = sel ? h.querySelector(sel) : null; + if (ref && ref.parentElement === h) h.insertBefore(b, ref); + else h.appendChild(b); + } + function init() { if (document.getElementById('dys-font-btn')) return; var st = document.createElement('style'); @@ -201,7 +228,7 @@ sync(); var h = host(); - if (h) { h.appendChild(b); return; } + if (h) { place(h, b); return; } // No home yet. Two of these sites build their masthead from JavaScript after // this runs, so a slot that does not exist at DOMContentLoaded may exist a @@ -215,7 +242,7 @@ var late = host(); if (!late || late.contains(b)) return; b.className = ''; - late.appendChild(b); + place(late, b); mo.disconnect(); }); mo.observe(document.body, { childList: true, subtree: true });