As they’re wont to do, a certain tweet was floating around the interwebs for a while the other week.

Recruiters be like:
We’re looking for someone who can connect to the database using CSS.
It’s been a hell of a long time since I last embarked on a quality shitpost project1, in fact it’s been so long that back then I probably didn’t even have the word shitpost in my vocabulary.
To that end, I was partially inspired by an earlier shitpost project based on a blockchain startup’s projection of their investors’ faces onto 3D cubes.2 Reminds me of the old days of the internet when everything was just weird.
I’m not looking to write a recipe here so I’ll spare you the life story. Instead, I’m going to talk about how I managed my own new shitpost project: sqlcss.xyz3

As the name might suggest, this is how you connect to a database using CSS. It only works in Chrome, unfortunately, but you can provide any SQLite database you like and query it via CSS.
How does it work?
-—
A new set of APIs affectionately known as Houdini4 give your browser the power to control CSS via its own Object Model in Javascript. In English, this means that you can make custom CSS styles, add custom properties, and so on.
Possibly the biggest feature to come out of this work is the CSS Paint Worklet5, which allows you to ‘paint’ on an element, not unlike the Canvas you know and love, and have the browser treat it as an image in CSS. There are some examples to play with at houdini.how6.
However, this worklet provides only a subset of the Worker API, and the canvas context itself is also heavily stripped down. The practical result of this is that your custom CSS painting code provides a smaller sandbox than you might have expected.
What does that mean? You have no network access, so you can kiss fetch
and XmlHttpRequest
goodbye. You have no drawText
functionality on the paint context. Various other JS APIs have also vanished, just in case you were hoping to work around some of those issues.
No need to worry, though. All is not lost. Let’s break this down into steps.
—
1. Setting up the database
This has to be the first step, to understand if a proof of concept is even possible.
There’s a library called sql.js
7. It’s quite literally a version of SQLite compiled into WebAssembly and old-skool ASM.js via emscripten. We can’t use the WASM version unfortunately, because it has to fetch a binary over the network. The ASM version doesn’t have this limitation though as all of the code is available in a single module.
While the PaintWorklet restricts network access inside the worker, you are still allow to import
code as long as it’s an ES6 Module. That means the file has to have an export
statement somewhere inside it. Unfortunately, sql.js doesn’t have an ES6 only build so I modified the script myself to make this work.
Now for the moment of truth: can I set up a database inside my worklet?
const SQL = await initSqlJs({ locateFile: file => `./${file}`, }); const DB = new SQL.Database();
Success! No errors. But no data either, so let’s fix that.
2. Querying the database
Easiest thing to do at the start is set up some fake data. Sql.js has a couple of functions to do precisely that.
DB.run('CREATE TABLE test (name TEXT NOT NULL)') DB.run( 'INSERT INTO test VALUES (?), (?), (?), (?)', ['A', 'B', 'C', 'D'] )
I’ve got my test table with some values in it now. I should be able to query this and get those values back, although I’m not sure how the result will be structured.
const result = DB.exec('SELECT * FROM test') console.log(result)
Results are there, as expected. It would be nice to actually render this result though.
3. Rendering the results, the easy way
I assumed