Toolium

A Throwaway Converter Still Needs to Work Without a Mouse

Hemanth Gedda7 min read

A few months ago I tried to use my own image converter without touching the mouse. I had been reading a post by a blind developer about what browsing feels like through a screen reader, and I wanted to know how Toolium held up. The answer arrived within eight seconds. I pressed Tab from the address bar, landed on the logo, pressed Tab again, and sailed past the upload zone to the footer links. The drop zone, the entire point of the page, did not exist for the keyboard. I had built it as a <div> with a click handler and a dashed border, and a <div> accepts focus from nobody.

I sat with that for a while. Someone who cannot use a mouse (a person with a tremor, a repetitive strain injury, a broken trackpad, or a screen reader driven by keyboard commands) would land on my converter, see a big friendly box that says "Drag your file here," and have no way to put a file in it. They would leave. I would never know, because nothing in my analytics separates a visitor who bounced out of boredom from a visitor the page locked out.

The upload zone that only exists for mouse users

My original drop zone was the pattern you find in a hundred tutorials: a styled <div> listening for dragover and drop, plus a click handler that opened a hidden file picker. It worked well for anyone holding a pointing device. For everyone else it was a decorative rectangle. Divs take no keyboard focus, announce no role, and respond to no key. The relevant WCAG guideline fits in one blunt sentence, that every function must work from a keyboard, and my rectangle failed it outright.

The repair costs almost nothing. Put a real <input type="file"> in the markup and pair it with a <label>. The browser then gives you everything the div lacked: the input joins the Tab order, Enter and Space open the picker, and a screen reader announces something like "Choose file, button." You can still style the label to look like the dashed box, and you can still layer drag-and-drop on top as an enhancement for mouse users. The two paths coexist.

One trap deserves a warning. If you hide the input with display: none or visibility: hidden, browsers pull it out of the accessibility tree and the Tab order, which recreates the original problem with extra steps. Hide it visually instead: clip it to a one-pixel square, position it absolutely, leave it rendered as far as the browser is concerned. Most CSS frameworks ship a utility class for this under a name like sr-only or visually-hidden. I copied mine from the article that started all this and moved on with my afternoon.

The result that arrives in silence

The keyboard failure embarrassed me. The screen reader failure was worse, because the user cannot even tell that something has gone wrong. I turned on VoiceOver, uploaded an image, and pressed convert. The spinner spun. The result card appeared with a download link. VoiceOver said nothing. A blind visitor at that moment is sitting in silence, waiting on a conversion that finished four seconds ago, wondering whether the button worked at all. The page had the answer and kept it to itself.

The fix is an aria-live region: an ordinary element, empty at page load, that screen readers watch for changes. When the conversion finishes, I write "File converted. Download ready." into it, and the screen reader speaks that sentence at the next natural pause, because I set aria-live="polite". Errors go into a second region with role="alert", which interrupts instead of waiting its turn. Two details matter in practice. The region must exist in the DOM before you inject text, since screen readers watch established regions and tend to miss ones created on the fly. And you should announce sparingly. A live region that narrates every progress percentage turns a quiet tool into a chatterbox nobody can think over.

Announcing covers half of the job. Moving focus covers the rest. After a conversion succeeds, I move keyboard focus onto the download button, so the next keystroke a keyboard user makes is the one they came for. Focus movement is a strong gesture and an easy one to abuse; yank it around on every state change and you disorient the same people you meant to help. I reserve it for the moment when the tool's one job is done and exactly one sensible next action exists.

The focus ring I deleted on purpose

Somewhere in Toolium's first stylesheet sat the line outline: none. I wrote it myself. The default focus ring clashed with the rounded cards and soft shadows, and every design tutorial I had absorbed treated the outline as browser cruft to strip. Stripping it takes away a keyboard user's cursor. A person tabbing through a page with no focus indicator presses Tab and guesses where they landed, like steering a mouse whose pointer stopped rendering.

Modern CSS removed the old excuse. The :focus-visible pseudo-class applies during keyboard navigation and stays quiet for mouse clicks, so the designer's complaint about ugly rings around clicked buttons no longer holds. You can design the ring to match your palette, give it an offset, make it handsome. Mine is now a two-pixel ring in the site's accent color, and I think the pages look better with it, which stings a little, given how long I fought against it.

Red borders and other things you cannot hear

My error states used to be a border color change. Paste malformed JSON into the formatter and the input's border turned from gray to red. That one signal excludes two audiences at once. Roughly one in twelve men has some form of color vision deficiency and may read that red as another shade of the gray it replaced. A screen reader conveys no border color, so a blind user hears no error at all, and we are back to sitting and waiting.

Now every error carries text. The message sits under the field, says what went wrong ("Unexpected comma at line 14"), and connects to the input through aria-describedby, so a screen reader reads the error whenever the user focuses that field. The border still turns red; color works fine as reinforcement on top of words. The same discipline covers labels. A placeholder evaporates the moment someone types, so every input on the site now has a real <label> tied to it with for and id. WCAG compresses all of this into three words, name, role, value: each control should expose what it is called, what kind of thing it is, and what state it holds. A native input with a proper label passes that test before you write a single ARIA attribute.

The forty-second visitor

I know how people use Toolium because I use tools like it the same way. You arrive from a search with one file to convert or one blob of JSON to inspect, you do the thing, and you leave, usually inside a minute. For a long while I let that brevity excuse the gaps. Nobody builds a relationship with a converter, I told myself, so the polish could wait for the features that keep people around.

The brevity argues the other way. A sighted mouse user who hits friction will squint and muddle through, because the whole errand costs forty seconds. A keyboard user who cannot reach the upload control has nothing to muddle through; for them the errand never completes. Short visits mean nobody extends you patience or loyalty, which means the first interaction is the entire relationship. WCAG organizes its rules under four principles, and the first two carry most of the weight for a site like mine: perceivable, meaning people can detect what the page is doing, and operable, meaning people can act on it. My converter failed both for whole categories of visitors, and I found out only because I went looking.

I will not pretend the site is finished. I have tested with VoiceOver on macOS and with the keyboard alone, and I fixed what those sessions surfaced, but I have not run NVDA or JAWS on Windows, where most screen reader users spend their time. I have not audited the dark theme's contrast beyond spot checks. The multi-step tools handle focus between steps worse than the single-shot converters do. I keep a list, and the list shrinks slower than I would like, because I build this site alone (the about page has the longer version of that story) and accessibility competes with everything else on the roadmap.

The other side of the ledger is short. A real file input behind the drop zone, a label on every field, a focus ring left alive, a live region that speaks when the work finishes: none of those fixes took me more than an hour, and most took minutes. The people who need them will rarely file a bug report, because they leave before you ever hear from them. If you build small tools, unplug your mouse some afternoon and try to finish one errand on your own site. My first failure took eight seconds to find, and those were the most useful eight seconds I spent on Toolium all year.