<?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: Mason Chen</title>
    <description>The latest articles on DEV Community by Mason Chen (@masnchen).</description>
    <link>https://dev.to/masnchen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4026520%2F4a474677-92fe-40e5-bd36-5cef69b5aeff.png</url>
      <title>DEV Community: Mason Chen</title>
      <link>https://dev.to/masnchen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/masnchen"/>
    <language>en</language>
    <item>
      <title>Feeling every button on macOS: haptic feedback built on the accessibility tree</title>
      <dc:creator>Mason Chen</dc:creator>
      <pubDate>Mon, 13 Jul 2026 01:15:02 +0000</pubDate>
      <link>https://dev.to/masnchen/feeling-every-button-on-macos-haptic-feedback-built-on-the-accessibility-tree-1po2</link>
      <guid>https://dev.to/masnchen/feeling-every-button-on-macos-haptic-feedback-built-on-the-accessibility-tree-1po2</guid>
      <description>&lt;p&gt;Most macOS hover states are a small color change. If your eyes are tired, or your vision is low, or the button is a subtle ghost control, you can easily miss it. I wanted to &lt;em&gt;feel&lt;/em&gt; when my cursor reached something clickable, the way a real switch has a detent. So I built &lt;a href="https://github.com/Mason363/Tactile" rel="noopener noreferrer"&gt;Tactile&lt;/a&gt;, a menu bar app that taps the trackpad's haptic motor whenever the pointer is over a clickable element, anywhere on the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;The whole thing is event-driven:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;CGEventTap&lt;/code&gt; watches cursor movement. When the mouse is still, nothing runs, so it costs 0% CPU.&lt;/li&gt;
&lt;li&gt;On movement, a throttled hit-test asks the macOS Accessibility tree what element is under the cursor. This is the same tree VoiceOver uses.&lt;/li&gt;
&lt;li&gt;A classifier decides whether that element is clickable (button, link, checkbox, menu item, tab, Dock item, and so on). Finder is a fun edge case: files and sidebar items expose an Open action rather than Press.&lt;/li&gt;
&lt;li&gt;If the cursor just entered a clickable element, the trackpad ticks once. Results are cached per element so continuous movement stays under a few percent of one core.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why the accessibility tree
&lt;/h2&gt;

&lt;p&gt;Reading the AX tree means Tactile learns only the &lt;em&gt;kind&lt;/em&gt; of element under the pointer, never its content and never your keystrokes. It also means coverage is broad by default: native apps, Finder, browsers, and Electron all expose accessibility roles. The privacy story falls out of the design rather than being bolted on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The haptics
&lt;/h2&gt;

&lt;p&gt;The default engine is the public &lt;code&gt;NSHapticFeedbackManager&lt;/code&gt;. There is also an optional engine that drives the trackpad actuator directly through a private framework, loaded at runtime with &lt;code&gt;dlopen&lt;/code&gt; and a safe fallback. That unlocks real strength differences and continuous vibration by pacing pulses on a dedicated thread, fast enough to feel like a buzz rather than a series of taps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser gap
&lt;/h2&gt;

&lt;p&gt;Some web controls are &lt;code&gt;div&lt;/code&gt;s with a click handler and &lt;code&gt;cursor: pointer&lt;/code&gt; and no ARIA role, so they never reach the accessibility tree. For those, an optional Chrome extension reports element rectangles over a local Unix socket, and a shared fire-region memory dedupes the two paths so nothing double-fires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility beyond haptics
&lt;/h2&gt;

&lt;p&gt;Because it started as a low-vision aid, Tactile gives three independent signals for the same event: feel (the tick), color (a green circle over clickable, red over destructive), and shape (an outline around the control, plus a crosshair and a small labeled pill telling you the element type and where it came from).&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;p&gt;It needs a Force Touch trackpad, since that is the hardware with the haptic motor, and macOS 14.6 or later. It is not notarized yet, so there is a one-time quarantine step, and the full source is available to inspect or build. MIT licensed, and the only network call it makes is the Sparkle update check to its own repo.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Mason363/Tactile" rel="noopener noreferrer"&gt;https://github.com/Mason363/Tactile&lt;/a&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>swift</category>
      <category>a11y</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
