<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Terence Gilligan</title>
    <description>The latest articles on DEV Community by Terence Gilligan (@terry_gilligan_e4bf4680b8).</description>
    <link>https://dev.to/terry_gilligan_e4bf4680b8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1936954%2F1891e7ec-0e88-4d3e-a55c-67a96186a3ee.webp</url>
      <title>DEV Community: Terence Gilligan</title>
      <link>https://dev.to/terry_gilligan_e4bf4680b8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/terry_gilligan_e4bf4680b8"/>
    <language>en</language>
    <item>
      <title>Why I Built a Privacy-First, 100% Offline Utilities Suite (And Left Out the Cookie Banners)</title>
      <dc:creator>Terence Gilligan</dc:creator>
      <pubDate>Mon, 02 Nov 2026 10:36:11 +0000</pubDate>
      <link>https://dev.to/terry_gilligan_e4bf4680b8/why-i-built-a-privacy-first-100-offline-utilities-suite-and-left-out-the-cookie-banners-olm</link>
      <guid>https://dev.to/terry_gilligan_e4bf4680b8/why-i-built-a-privacy-first-100-offline-utilities-suite-and-left-out-the-cookie-banners-olm</guid>
      <description>&lt;p&gt;100% Offline Utilities Suite &lt;/p&gt;

&lt;p&gt;We’ve all been there: you need to quickly optimize an image, format some data, or run a quick utility. You search online, click a link, and immediately get hit with a cookie banner, an aggressive paywall, or a forced sign-up just to do a basic task. Worse, you're left wondering if your data is being shipped off to some third-party cloud server.&lt;/p&gt;

&lt;p&gt;So, I built what I actually wanted to use: online-utilities.com.&lt;/p&gt;

&lt;p&gt;It’s a collection of browser-native utilities built with a simple philosophy: no individual tracking, no sign-ups, and 100% free to use. (I use lightweight Google Analytics purely for anonymous site traffic stats, but there are no user accounts, no tracking cookies, and no data harvesting.) Because everything runs locally in your browser as a Progressive Web App (PWA), it even works completely offline once loaded.&lt;/p&gt;

&lt;p&gt;PS.I live on a small farm hence the building utilities. &lt;/p&gt;

&lt;p&gt;Have a great week people&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showcase</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>My accessibility scanner cried wolf on a real site, so I fixed the scanner and put it in CI</title>
      <dc:creator>Terence Gilligan</dc:creator>
      <pubDate>Sat, 31 Oct 2026 10:23:34 +0000</pubDate>
      <link>https://dev.to/terry_gilligan_e4bf4680b8/my-accessibility-scanner-cried-wolf-on-a-real-site-so-i-fixed-the-scanner-and-put-it-in-ci-2c6j</link>
      <guid>https://dev.to/terry_gilligan_e4bf4680b8/my-accessibility-scanner-cried-wolf-on-a-real-site-so-i-fixed-the-scanner-and-put-it-in-ci-2c6j</guid>
      <description>&lt;p&gt;The European Accessibility Act has been enforceable for over a year now, and the tooling situation for developers is still awkward: every scanner flags everything, so teams learn to ignore the red — which is worse than having no scanner at all.&lt;/p&gt;

&lt;p&gt;I built a WCAG 2.2 audit engine that runs as a GitHub Action. The design rule is &lt;strong&gt;"inform aggressively, block conservatively"&lt;/strong&gt;: the build fails only on determinate WCAG Level-A defects. Warnings, heuristics, and things automation genuinely can't prove still get reported — they just don't hold your deploy hostage.&lt;/p&gt;

&lt;p&gt;Then I ran it on a real production site, and my own scanner was the thing that was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The finding that turned out to be my bug
&lt;/h2&gt;

&lt;p&gt;The target: a live veterinary-practice PWA (&lt;a href="https://vetcarepros.com" rel="noopener noreferrer"&gt;vetcarepros.com&lt;/a&gt;) — real users, real stakes. The first audit came back with &lt;strong&gt;2 Level-A errors&lt;/strong&gt;: elements unreachable by keyboard and no visible focus indicator, both anchored on the same component — the site's reCAPTCHA badge.&lt;/p&gt;

&lt;p&gt;Serious if true. Keyboard traps are exactly the kind of thing the EAA cares about.&lt;/p&gt;

&lt;p&gt;Except it wasn't true. The badge is a presentational utility: &lt;code&gt;tabindex="-1"&lt;/code&gt;, &lt;code&gt;role="presentation"&lt;/code&gt;, deliberately removed from the tab order &lt;em&gt;by design&lt;/em&gt; — the same pattern Radix and Headless UI use for form shims. My probe was correctly &lt;em&gt;detecting&lt;/em&gt; the element and correctly &lt;em&gt;observing&lt;/em&gt; it couldn't be tabbed to. What it couldn't do was tell "hidden on purpose and declared so" apart from "broken."&lt;/p&gt;

&lt;p&gt;That distinction — between a finding and a false positive — is the whole game. A scanner that can't make it trains developers to dismiss every result it produces.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the fix actually looks like
&lt;/h2&gt;

&lt;p&gt;The detector now suppresses only when &lt;strong&gt;both&lt;/strong&gt; signals are present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the element is &lt;em&gt;declared&lt;/em&gt; non-interactive (&lt;code&gt;tabindex="-1"&lt;/code&gt;, &lt;code&gt;role="presentation|none"&lt;/code&gt;), &lt;strong&gt;and&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;it has trusted-widget provenance (a known embed source like reCAPTCHA/App Check, or a recognized wrapper).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An element that hides itself without declaring it still fails as a conclusive Level-A error. A visible embed missing either signal still fails. You can't escape a finding by slapping &lt;code&gt;tabindex="-1"&lt;/code&gt; on it — the suppression needs the full story.&lt;/p&gt;

&lt;p&gt;And here's the part I'm more proud of: the suppressed findings aren't deleted. They're reported as &lt;code&gt;info&lt;/code&gt; with &lt;code&gt;disposition: false_positive&lt;/code&gt; — disclosed in the findings report, the CSV export, and the VPAT remarks as "observation(s) dispositioned as not-applicable." Hiding them would be the same lie as over-flagging, just quieter.&lt;/p&gt;

&lt;p&gt;Same site, re-scanned: &lt;strong&gt;0 errors, 0 warnings, 2 disclosed suppressed findings.&lt;/strong&gt; That's what "clean" should look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honesty problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;The deeper issue in a11y tooling is grading honesty. Most WCAG criteria can't be fully proven by automation — "is this title meaningful?" isn't a regex. The engine's answer is boring but strict: if the checks can't prove conformance, the row says &lt;code&gt;Not Evaluated&lt;/code&gt; and names the manual task you'd need to do. It never claims &lt;code&gt;Supports&lt;/code&gt; because nothing went wrong; it claims it only when conclusive checks ran clean on counted elements.&lt;/p&gt;

&lt;p&gt;A compliance report that hedges honestly is worth more than one that's confidently wrong — because the former is &lt;em&gt;evidence&lt;/em&gt; and the latter is a liability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get in CI
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Build fails on determinate Level-A defects only (&lt;code&gt;fail-on: 'level-a'&lt;/code&gt;, or &lt;code&gt;'any-error'&lt;/code&gt;, or &lt;code&gt;'never'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;PR comment + step summary with the findings table&lt;/li&gt;
&lt;li&gt;Same audit run powers a downloadable VPAT/ACR and findings CSV in the portal&lt;/li&gt;
&lt;li&gt;Optional Merkle hash-chain verification of your audit trail (&lt;code&gt;verify-chain: 'true'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;EU data residency — sovereign partitions, geo-routed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perimeter-only:&lt;/strong&gt; it scans the deployed URL. Your source code never leaves your repo — and the action source is public so you can verify that yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Free during the developer beta — no card, 90-day keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.compliance-audit.eu/v1/demo/keys &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"companyName": "your-project"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Terrygilligan/compliance-audit-verify-action@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COMPLIANCE_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;tenant-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COMPLIANCE_TENANT_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;target-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://your-preview-deploy.example.com'&lt;/span&gt;
    &lt;span class="na"&gt;fail-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;level-a'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Action repo: &lt;a href="https://github.com/Terrygilligan/compliance-audit-verify-action" rel="noopener noreferrer"&gt;Terrygilligan/compliance-audit-verify-action&lt;/a&gt; (MIT). Zero-dependency — it's a 300-line Node script, read it before you run it. Portal with the live audit sandbox (no key needed): &lt;a href="https://compliance-audit.eu" rel="noopener noreferrer"&gt;compliance-audit.eu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bugs and false positives — especially false positives — go in the repo's issues. If your site trips a wrong finding, that's a bug in my detector, not your markup, and I want to see it.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>webdev</category>
      <category>githubactions</category>
      <category>compliance</category>
    </item>
  </channel>
</rss>
