Back to Projects
ReactTypeScriptReduxAnt DesignChrome ExtensionDjangoJWTNetlifyHeroku

Probable Dribble

A readability rating tool for people with low vision. A Chrome extension spots when you are on a site we index, hands you a short survey, and the institution that owns the site watches the scores and the written suggestions land on a dashboard.

Probable Dribble

Project Overview

Probable Dribble was a readability rating tool for people with low vision. An institution registers its website, a visitor with the browser extension installed lands on that site, the extension notices the URL is one we index, and offers them a short survey. They score the site out of five on things like contrast, font size and how easy the layout is to follow, leave a written suggestion at the end, and the institution watches all of it land on a dashboard. Three repositories, all from June 2022, all still sitting on GitHub exactly as we left them.

The extension is the smallest piece and the one I still like. Manifest V3, four files of my own code and a vendored Bootstrap folder that dwarfs them. It asks Chrome for the active tab, sends the URL to a Django backend, and if that call throws it rewrites the popup to say "This site is not indexed" and deletes the button. Failure and "not in the database" are the same code path, which is lazy and worked fine, because in both cases there is nothing for you to do. If the site is indexed you get a Take Survey button, and clicking it opens the survey app with the current URL hung on the query string. That query string is the entire integration between the two front ends. No messaging, no storage, no shared state. One parameter.

The survey itself is an Ant Design Steps wizard: five tabs of two questions each, then a sixth for the suggestion box. The questions are fetched from the backend rather than hardcoded, which was the right call, since the whole point was that institutions would eventually pick their own. Submitting posts a questionnaire with the website URL, waits for the id, then fires off one POST per answer in a loop. Ten answers, ten requests, no transaction, no retry. If the fourth one fails you get a questionnaire with three answers on it and nobody finds out.

The rating control has a bug I did not notice for weeks. Each of the five stars is its own piece of `useState`, and the click handler only records a score if all five are currently false. Pick four, change your mind, click three: the guard sees a star already lit, so it sets that one back to false and records nothing. You cannot change your answer, you can only clear it, and the only way to actually change it is to clear it first and then click again. There is also a real checkbox input sitting under each card with `name="jobb"` and values like "Design" and "Code" left over from the template, wired to nothing. On an accessibility project. I know.

The dashboard is a Create React App in TypeScript with Chakra, PrimeReact and MUI all in the same package.json, which tells you roughly how the component decisions were made that month. Institutions sign up, add a website, and see their questionnaires over the last ten days, per question rating trends, and a table of written suggestions. Auth is a JWT in localStorage sent as `Authorization: JWT <token>`, the Djoser default we never changed. The graph endpoints answer with keys named `10`, `09`, `08` down to `yesterday` and `today`, and the chart components read them by hand, in order, one line each. There is a `Statistics.tsx` in there that returns the word "Statistics" in a div.

The backend was Django on a free Heroku dyno. Heroku killed free dynos a few months later, so `probable-dribble.herokuapp.com` is a 404 now and everything downstream of it is a museum piece. The survey form is still up on Netlify and still loads, it just asks you to rate a question mark. The screenshot at the top of this page is exactly that, an honest picture of a front end that outlived its API.

Almost nothing here is named the way I would name it now. The repos are called `probable-trible`, which is what GitHub suggested when I could not be bothered, and the deployed survey lives at `probable-dribble-survery.netlify.app` because I typed survey wrong in the Netlify subdomain and then linked it that way from the extension. The extension's manifest still lists a teammate as the author. Four years on, all three are still true.

Key Features

  • Manifest V3 extension that reads the active tab URL and asks the backend whether the site is indexed
  • Popup that offers the survey when there is a match and says so plainly when there is not
  • Six step survey wizard, ten scored questions plus a free text suggestion
  • Questions served by the backend, so an institution picks its own rather than taking ours
  • The site being rated is passed between the two front ends as a single URL query parameter
  • Institution dashboard for adding a website and reading back its questionnaires
  • Ten day trend charts for questionnaire volume and per question ratings
  • Lazy loaded suggestions table, JWT auth held in localStorage

Challenges & Solutions

Three separate front ends that only ever talk to each other through one query parameter

Submitting a questionnaire as ten sequential POSTs with no transaction behind them

A star rating built from five independent booleans, which quietly refuses to let you change your answer

Manifest V3 permissions, where reading the active tab URL is most of what the extension is allowed to do

Telling 'this site is not indexed' apart from 'the request failed', which the extension never bothers to do

Key Learnings

An accessibility project is the worst place to hang a click handler on a div and leave the real input inert
Guard conditions on five separate useState booleans are a state machine you did not mean to write
A fan out of one request per answer needs a bulk endpoint, or you ship half filled records
Free tier hosting decides how long your project lives, and Heroku decided this one
Pick the component library before you install three of them

Project Info

Timeline

June and July 2022

Team

Small team, three repos between us

Status

Archived, the backend went with Heroku's free tier

Tech Stack

Dashboard

React 18TypeScriptCreate React AppRedux ToolkitPrimeReactChakra UIChart.jsaxios

Survey

React 18TypeScriptAnt DesignFramer Motion

Extension

Chrome Extension (Manifest V3)JavaScriptBootstrap 5

Backend

DjangoDjango REST FrameworkJWT

Deployment

NetlifyHeroku