a11y.css
a11y.css copied to clipboard
[webextension] — Focus order visualizer
Not sure it's easy nor doable but I'd want to give this a try:
JavaScript
- list focusable elements,
- log them in proper order,
- get their respective coordinates,
- set on
html
tag two custom properties for each element (say,--1-x
and--1-y
, etc.), - probably set a
data-*
attribute on each element, with their respective order as value.
CSS
If you had a look to chaarts, you might have guessed what I'm trying to do.
Using clip-path
and a polygon()
value with enough points (hardcoded arbitrary value, or generated via JavaScript?) directly on the html
element, we should be able to draw a line between each element and to display their order thanks to the data-attribute
and a pseudo-element.
Any thought?
It will probably be hard on perf, so any thought welcome.
Resources
- Adrian Roselli 'Focus Interactive Controls' bookmarklet is very interesting.
- @notabene came up with his own bookmarklet:
javascript: (function () {
try {
var s = document.createElement('style');
var b = document.getElementsByTagName('body').item(0);
var counter = 0;
s.innerHTML = '.FocusOrder{position:absolute;border:.1rem solid #00f;border-radius:50%;background:#00f;color:#fff;padding:.25rem .5rem;font-family:\'Segoe UI\',-apple-system,BlinkMacSystemFont,Roboto,Oxygen-Sans,Ubuntu,Cantarell,\'Helvetica Neue\',sans-serif;text-align:center;min-width:1rem;line-height:1;box-shadow:.2rem .2rem .2rem rgba(0,0,0,.5);margin-left:-.5rem;margin-top:-.75rem}';
document.getElementsByTagName('head').item(0).appendChild(s);
for (var e = document.querySelectorAll("a[href], area[href], input:not([disabled]), button:not([disabled]), select:not([disabled]), textarea:not([disabled]), iframe, object, embed, summary, [tabindex]:not([tabindex='-1']), [contenteditable=true], video[controls], audio[controls]"), t = 0; t < e.length; t++) ! function (t) {
var rect = e[t].getBoundingClientRect();
if(rect.top != 0 && rect.left != 0) {
var sp = document.createElement('span');
sp.classList.add('FocusOrder');
sp.innerText = ++counter;
sp.setAttribute('style',`top:${rect.top}px;left:${rect.left}px`);
b.appendChild(sp);
}
}(t)
} catch (e) {
console.log(e)
}
})()
My bookmarklet code is just a basis to show how focus is ordered on a page but it lacks a few things:
- Elements that are not displayed have no tooltip (if top and left are not computed, they are "0")
- This is static, so if the page moves tooltips will go awry. The right thing to do should be a
::before
for every focusable element, plus relative position as a minimum, I guess.
BTW I'm writing an article on the bookmarklet I wrote, but to be clear most of my code comes directly from @aardrian (giving credit where credit's due as early as possible).
There might be something you can use from my reading order visualizer: https://adrianroselli.com/2019/04/reading-order-bookmarklet.html
I found I could not throw the numbers into a page using ::before
or ::after
since that often broke site styles. That is why all my numbers live at the end of the DOM and are absolutely positioned.
BTW I'm writing an article on the bookmarklet I wrote, but to be clear most of my code comes directly from @aardrian (giving credit where credit's due as early as possible).
Here: https://nota-bene.org/Focus-order-bookmarklet
Newcomer: @KittyGiraudel came up with a micro-lib to list focusable elements. I guess this selector is comprehensive, or will be at some point. :)
- Needs
object
,embed
,summary
. - I like the addition of checking for
inert
, but until it is supported it might give false positives (negatives?). - I also like checking for non-negative
tabindex
. -
audio
andvideo
might need to check for thecontrols
attribute. -
contenteditable
is a good addition too, even if it makes me queasy.
@ffoodd Thank you for the shoutout. :) @aardrian Thanks for sharing your thoughts. I have a few further questions/comments:
-
I’ve discovered that only the first
<summary>
element within a<details>
element should be focusable. I also read that a<details>
element without a<summary>
element should be focusable, but that cannot be represented with a CSS selector so I’ll omit it. -
Good catch on the
controls
attribute of the<audio>
and<video>
elements. I issued a fix. -
The
<embed>
and<object>
elements do not appear to be focusable by default and I cannot find much literature around them. Do you have any further information to share? From focus-trap/tabbable:The tabbability of
<iframe>
s,<embed>
s,<object>
s,<summary>
s, and<svg>
s is inconsistent across browsers, so if you need an accurate read on one of these elements you should try giving it a tabindex. (You'll also need to pay attention to the focusable attribute on SVGs in IE & Edge.) But you also might not be able to get an accurate read — so you should avoid relying on it.
Support for summary
in focusable-selectors
is coming in this PR: https://github.com/KittyGiraudel/focusable-selectors/pull/9