← All posts

3 min read

Accessibility signals: dark mode and reduced motion as aggregates

Tracely now reports dark mode and reduced motion preferences as aggregate shares, plus a new hourly rollup for keyboard and pointer activity that shipped today.

A question I kept running into while building dashboards: should dark mode come first? Is anyone actually using reduced motion? These are design-system decisions that deserve real data, and most analytics tools either don't collect the signals at all or bury them somewhere per-user, which is worse. Tracely's answer is the same as everywhere else in the product: collect them as aggregates, and only aggregates.

Two preference cards

The tracker reports two browser preferences alongside each pageview:

Color scheme. What prefers-color-scheme says: dark, light, or unknown when the browser doesn't report it. The dashboard shows the share of pageviews for each, as a card on the site Overview and on the Technology screen.

Reduced motion. Whether prefers-reduced-motion: reduce was set, versus standard. Same treatment: a card next to color scheme, showing the split for the selected period.

That's the whole feature, and deliberately so. A media-query preference is a small signal on its own, but small signals are exactly what fingerprinting is made of when you stitch enough of them together per-visitor. So Tracely never shows you "this visitor prefers dark mode" — you get "41% of pageviews this week came from browsers preferring dark mode," which is the number you actually need to decide whether your dark theme deserves more polish.

If your dark-mode share is high and your dark theme is an afterthought, that's a concrete, prioritizable finding. If reduced motion is non-trivial and your homepage is a parallax festival, same thing.

Accessibility signals, the behavioral half

Preferences tell you what visitors ask for. The opt-in Accessibility signals toggle tells you what they do. When it's on, the tracker sends one "A11y page summary" event per page load with activity, flushed when the tab is hidden, unloaded, or navigated in an SPA. The payload is four counters: pointer_interactions, keyboard_interactions, focus_visible_events (capped in the tracker so a stuck key can't flood ingest), and skip_link_used as a 0 or 1.

From those you can answer questions that are usually pure guesswork: what fraction of interaction on this site is keyboard-driven? Do focus-visible outlines actually appear in the wild, or is everything mouse-first? Does anyone use the skip link you dutifully added? Skip-link use is counted as distinct sessions, not raw clicks, so one enthusiastic keyboard user doesn't inflate the number.

What shipped today: hourly rollups

Until this morning, the accessibility panel on the Overview summed those counters out of raw events on every load. That's fine at small volume and increasingly rude to the database as event counts grow. Today I shipped a proper rollup: a11y_summary_hourly_stats, one row per site per hour, with summed pointer, keyboard, and focus-visible counts plus distinct skip-link sessions.

Ingest keeps the rollup fresh as events arrive. The Overview reads the rollup first and only falls back to scanning raw events for cases the rollup can't serve — the business-hours filter, or a window the rollup hasn't covered yet.

There's also a rebuild command for the paths that bypass live ingest — imports, seeds, backfills:

php artisan tracely:rebuild-a11y-summary-hourly-stats {site} --days=90

Pass --all to rebuild every site. It rebuilds from the raw "A11y page summary" events, so the rollup is always disposable and reconstructible — the same rule every other rollup in Tracely follows.

The pattern

This is a small feature, but it's a good example of the shape I want everything in Tracely to have: a low-entropy signal, stored aggregate-first, summarized hourly, cheap to query, and impossible to turn back into a story about one person. More on the non-goals side of that in the privacy page.

If accessibility shares turn out to be useful to people, there's more in this vein I'd like to do — but preferences and interaction modality felt like the right place to start, because they're the ones that change design decisions.

accessibility product

Try Tracely on your site

One snippet, cookieless, first-party. See your traffic in minutes without handing it to an ad network.

Start free

Get new posts by email

Short product notes twice a week plus the occasional deep dive. No tracking pixels in the emails, and you can unsubscribe anytime.

Your address is used only to send new posts. No open tracking.