From 12f1fd54dfffa9ff18ada8da54422250b1c4af55 Mon Sep 17 00:00:00 2001 From: null Date: Thu, 10 Sep 2026 19:12:23 -0500 Subject: [PATCH] docs: no em dashes in what ae966e8 added The rule is none in replies, commits, issues, docs or site copy, and the last commit put 40 of them into five documents, two scripts and two comments. Nothing reached the site: dist/ has none, and the content validator refuses U+2014 in src/data. Rewritten by sentence rather than substituted, so the punctuation carries the same meaning it did. scripts/lib/css-audit.js is a copy of another repository's file and its 20 were repunctuated with it. Its header now says so, because "lifted verbatim" would otherwise be a claim a byte diff disproves. The logic is untouched, and the sweep still reports zero findings on all ten devices. The four Batch 19 issue bodies were edited in the tracker for the same reason. Co-Authored-By: Claude Opus 5 (1M context) --- docs/TOOLS.md | 2 +- docs/design/OVERHAUL_PLAN.md | 6 +-- docs/history/DEVELOPMENT_LOG.md | 16 ++++---- docs/qa/ClaudeQAPlan.md | 4 +- scripts/device-sweep.mjs | 8 ++-- scripts/lib/css-audit.js | 52 ++++++++++++------------ src/components/content/ContentBlocks.jsx | 2 +- src/index.css | 2 +- 8 files changed, 47 insertions(+), 45 deletions(-) diff --git a/docs/TOOLS.md b/docs/TOOLS.md index 75860e8..235cd9e 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -227,7 +227,7 @@ still report a scrollWidth equal to the viewport. Eight kinds of finding, of which `clipped`, `past_viewport` and `document_scrolls` are blocking. Exit 0 clean, 1 findings, 2 nothing swept. -It exists because #214 — the header CTA clipped at iPad portrait — was fixed, +It exists because #214, the header CTA clipped at iPad portrait, was fixed, checked in a desktop window sized to 768, and released still broken. A desktop window at 768 has a scrollbar, so the layout viewport was ~753px and the `md` breakpoint the fix was about never engaged. A device profile has no scrollbar diff --git a/docs/design/OVERHAUL_PLAN.md b/docs/design/OVERHAUL_PLAN.md index ac1b44b..0089b5f 100644 --- a/docs/design/OVERHAUL_PLAN.md +++ b/docs/design/OVERHAUL_PLAN.md @@ -826,7 +826,7 @@ The build refuses copy that breaks any of this: see `scripts/lib/content.js`. - Mobile section padding: `py-16` (64px) - Desktop section padding: `py-24` (96px) - Card padding: `p-4 md:p-6` -- **Standalone link lists: `space-y-4`, not `space-y-2`** — see the tap-target +- **Standalone link lists: `space-y-4`, not `space-y-2`.** See the tap-target rule below. This is a change from what the rest of this file describes, made 2026-09-10; it affects the footer columns, the privacy contents and the related-links lists. @@ -839,7 +839,7 @@ takes those 8px back out of the layout, so the line the link sits on does not move. **It only works if the list leaves 16px between rows**: the negative margin does not shrink the box, only its effect on layout, so at `space-y-2` each link's box reached 8px into a gap its neighbour was already reaching 8px -into. They overlapped, and `getBoundingClientRect` still read 33px — a target +into. They overlapped, and `getBoundingClientRect` still read 33px: a target that measured right and was not there. Three rules follow from that: @@ -860,7 +860,7 @@ Three rules follow from that: 768 to 1023 gets the `Sheet` menu. The desktop row cannot fit 768: brand, six nav links and the CTA want 787px of natural width against 736px of container, so flex shrank the CTA and wrapped its label and it *still* overflowed. The -menu is the better tablet experience regardless — 44px rows instead of 17px +menu is the better tablet experience regardless: 44px rows instead of 17px ones, and the Services and Industries submenus are reachable, where the desktop dropdowns open on hover and a touch device has no hover. diff --git a/docs/history/DEVELOPMENT_LOG.md b/docs/history/DEVELOPMENT_LOG.md index 6ee2bd6..c7be14f 100644 --- a/docs/history/DEVELOPMENT_LOG.md +++ b/docs/history/DEVELOPMENT_LOG.md @@ -46,10 +46,10 @@ sequence would have implied more. ## Entries -### 2026-09-10 — A device sweep, and a fix that was released without ever being exercised +### 2026-09-10: a device sweep, and a fix that was released without ever being exercised -Every page rendered on ten emulated phones and tablets — real Playwright device -profiles rather than a resized window — and measured. **190 page loads, 21 +Every page rendered on ten emulated phones and tablets, on real Playwright +device profiles rather than a resized window, and measured. **190 page loads, 21 blocking and 530 high findings** on pages that had already passed Batch 11 and Batch 16. Three defects, all live in production. Filed as #238 to #241 under Batch 19. @@ -76,7 +76,7 @@ of being quietly squeezed past the edge. **#239, the Cisco mark.** It ships in a 700x700 canvas it fills 66% of, so `object-contain` rendered it small beside 8x8's and both pages compensated with -`scale-[1.5]` and `scale-[2]` inside `overflow-hidden` — cutting 13px off the +`scale-[1.5]` and `scale-[2]` inside `overflow-hidden`, cutting 13px off the trademark on `/` and 24px on `/about`. Cropping the asset's own `viewBox` to the artwork and dropping the scale renders it at 62x46 on `/`, the same size as the 8x8 logo beside it, with nothing clipped. @@ -90,8 +90,8 @@ whichever painted last. Both versions measure 33px and neither is 33px. **A target that measures right and is not there is worse than one that measures wrong.** The rule that survived is in `design/OVERHAUL_PLAN.md`: `.tap-target` is `inline-block`, and every list that uses it moves to `space-y-4`. Then a -second surprise — a 39x17 link grown to 39x36 stops being row-shaped and starts -being judged as a compact target wanting 44px — so footer links also take +second surprise: a 39x17 link grown to 39x36 stops being row-shaped and starts +being judged as a compact target wanting 44px, so footer links also take `block` and the whole row becomes the target. **#241 is the instrument.** `scripts/device-sweep.mjs` plus @@ -103,7 +103,7 @@ the viewport while content is sliced off the right edge. Not wired into a guard that cannot run on a clean clone is one that gets skipped. Worth recording that the original in the other repository declares device profiles and then only calls `setViewportSize`, so its `isMobile` and `deviceScaleFactor` -never take effect — it is a width sweep wearing a phone's clothes, which is the +never take effect. It is a width sweep wearing a phone's clothes, which is the same blind spot in a different form. **Proven, not assumed.** After the fixes the sweep reports zero blocking and @@ -112,7 +112,7 @@ sweep reported it again: `clipped x1` and `media_overflow x1` for the logo, `past_viewport x19` for the header, `touch_target x342` for the footer. The first attempt at the header proof reverted only the nav's breakpoint and left the CTA at `lg`, so the element that overflows was not on screen and the sweep -correctly reported nothing — a wrong mutation, not a blind checker, and worth +correctly reported nothing. A wrong mutation, not a blind checker, and worth writing down because it looks identical to a checker that has stopped working. Exit codes were proven separately: 2 for an unreachable origin and for an unknown `--devices` name, because "nothing was swept" must never read as diff --git a/docs/qa/ClaudeQAPlan.md b/docs/qa/ClaudeQAPlan.md index 08237f3..210c33c 100644 --- a/docs/qa/ClaudeQAPlan.md +++ b/docs/qa/ClaudeQAPlan.md @@ -99,8 +99,8 @@ and both were invisible to the instrument being used. **A window sized to 768 is not 768.** It has a scrollbar, so the layout viewport is about 753, so the `md` breakpoint never engages and the desktop layout the -check is about is never on screen. That is how #214 — the header CTA clipped at -iPad portrait — was fixed, checked at "768", released, and was still 25px past +check is about is never on screen. That is how #214, the header CTA clipped at +iPad portrait, was fixed, checked at "768", released, and was still 25px past the right edge on every page. A device profile has no scrollbar inset. Check a breakpoint on a device, or on an emulated one; never on a window you dragged. diff --git a/scripts/device-sweep.mjs b/scripts/device-sweep.mjs index 5310309..d32917e 100755 --- a/scripts/device-sweep.mjs +++ b/scripts/device-sweep.mjs @@ -7,7 +7,7 @@ // node scripts/device-sweep.mjs --devices "iPhone SE,iPad Mini" // node scripts/device-sweep.mjs --report /tmp/sweep.md --shots /tmp/sweep // -// Exit codes: 0 nothing found. 1 findings. 2 NOTHING WAS SWEPT — playwright +// Exit codes: 0 nothing found. 1 findings. 2 NOTHING WAS SWEPT: playwright // missing, chromium unlaunchable, or no sitemap. Two is not a pass. // // ## Which incident motivated it @@ -171,8 +171,8 @@ if (!loads) { process.exit(2) } -// The same defect on six devices is one defect. Group on what identifies it — -// route, element, text — and keep the device list, because "only iPad Mini" +// The same defect on six devices is one defect. Group on what identifies it +// (route, element, text) and keep the device list, because "only iPad Mini" // versus "all ten" is the difference between a breakpoint bug and a layout bug. const groups = new Map() for (const finding of findings) { @@ -203,7 +203,7 @@ if (REPORT) { for (const row of group) { lines.push( `- **${row.route}** ${row.kind}: ${row.says || ''}`, - ` - \`${row.path}\`${row.text ? ` — text: ${JSON.stringify(String(row.text).slice(0, 60))}` : ''}`, + ` - \`${row.path}\`${row.text ? `, text: ${JSON.stringify(String(row.text).slice(0, 60))}` : ''}`, ` - on ${[...row.devices].join(', ')}`, row.detail ? ` - \`${JSON.stringify(row.detail)}\`` : '', ) diff --git a/scripts/lib/css-audit.js b/scripts/lib/css-audit.js index d2086a1..0584cb7 100644 --- a/scripts/lib/css-audit.js +++ b/scripts/lib/css-audit.js @@ -6,7 +6,7 @@ // // It measures rather than guesses. Every box is compared against its nearest // CLIPPING ancestor instead of document.scrollWidth, which lies the moment any -// container carries overflow-x: hidden or clip — and this site's body does, so +// container carries overflow-x: hidden or clip, and this site's body does, so // a page can slice content off its right edge and still report a scrollWidth // equal to the viewport. That is exactly how the header CTA at iPad portrait // survived a fix and a release. @@ -21,7 +21,9 @@ // where the thresholds were argued out and where the comments explaining each // one were written. Copied rather than shared because the two repositories have // no common package; if a threshold changes in one, it does not change in the -// other. The driver here differs from that one in a way that matters: css-qc +// other. The logic is that file's, unchanged. The prose is not byte-identical: +// this repository does not use em dashes, so the comments and the two report +// strings were repunctuated. Diff it on words, not bytes. The driver here differs from that one in a way that matters: css-qc // declares Playwright device profiles but only ever calls setViewportSize, so // its deviceScaleFactor, isMobile and hasTouch fields never take effect and it // is a width sweep wearing a phone's clothes. @@ -74,7 +76,7 @@ export const audit = function audit() { /** * Inside a closed disclosure, and therefore not on screen at all. * - * Chrome does not `display: none` a closed `
` — it skips the + * Chrome does not `display: none` a closed `
`. It skips the * subtree with `content-visibility`, and the descendants keep reporting * layout boxes at their unconstrained size. The signature form in the * documents table measured 149px wide at x=255 on a 320px screen while the @@ -149,7 +151,7 @@ export const audit = function audit() { if (scrollerOf(el)) continue; // Contained by something that clips: the reader does not see this past the - // edge, they see it cut off — which check 2 reports, with the clipper named. + // edge, they see it cut off, which check 2 reports, with the clipper named. // Reporting it here as well was the single largest source of noise in the // first run: every `truncate` in the admin has a child span whose rect runs // off the viewport by design, ellipsis and all. @@ -223,7 +225,7 @@ export const audit = function audit() { const side = worst.rect.right - box.rect.right >= box.rect.left - worst.rect.left ? "right" : "left"; // `text-overflow: ellipsis` is a container saying "I will cut text off and - // show that I did". That is an affordance, not silent loss — the reader can + // show that I did". That is an affordance, not silent loss: the reader can // see there is more. It stops being one the moment something interactive or // replaced is inside, because a button behind an ellipsis is still a button // nobody can press. @@ -238,14 +240,14 @@ export const audit = function audit() { * The first version of this asked whether a control was present at all, and * that is too coarse for the commonest shape in the admin: a truncated cell * whose text *is* a link. `span.block.truncate > a` reports the anchor's - * full 189px box against a 144px cell, so the anchor counted as swallowed — + * full 189px box against a 144px cell, so the anchor counted as swallowed, * while on screen 161px of it is visible, ellipsised, and perfectly * clickable. Three blocking findings on the projects board, all of them the * repository link reading `null/Privacy-Period-Tr...`, none of them a fault. * * What the rule is really protecting against is a control the clip puts out * of reach, so measure that: how much of it is left inside the box. Below - * the 24px WCAG floor — or its own width, for a control smaller than that — + * the 24px WCAG floor (or its own width, for a control smaller than that) * there is nothing to press and the ellipsis is not an affordance any more. */ const MIN_AIMABLE = 24; @@ -288,7 +290,7 @@ export const audit = function audit() { }, says: `${leaves.length} element(s) cut off by ${Math.round(escape(worst))}px past the ${side} edge ` + - `of an overflow-x:${box.cs.overflowX} box — no scrollbar, no hint`, + `of an overflow-x:${box.cs.overflowX} box, with no scrollbar and no hint`, }); } @@ -318,7 +320,7 @@ export const audit = function audit() { if (!rec) continue; // A control inside a