The site uses cookies that you may not want. Continued use means acceptance. For more information see our privacy policy.

Working on a new theme.

The joy of blogging?

The image is my placeholder for a default thumbnail. A recent update to WordPress made it so my editor styles aren’t applied, so I’m writing this on a white background with black text. Not my favorite.

I know that so-called block themes (made up of WordPress blocks rather than being PHP-first) is the general direction of the WordPress ecosystem, so I decided I’d start working on one. It’s a mixed bag, so far. The documentation ranges from adequate to not so much, and the search results on popular search engines is polluted by years of the old system, such that trying to understand how to do a thing means you’re more likely to be told how you used to do the thing.

The idea of building everything in the site editor is intriguing but not there at all in terms of feature completeness. WYSIWYG (wizzy-wig, or what you see is what you get) has never been true to its awkward acronym, and that remains true in WordPress-land. I try to do more via theme.json and building parts, patterns, templates, but that’s also not great.

One of the biggest drawbacks is how little basic knowledge of HTML and CSS translate to the new stuff. Rather than leaning on that built-up knowledge, the keywords and systems are their own designs within block themes, meaning you have to dig around to figure out how to say the thing you can already say perfectly fine in the common languages of the web.

I’m trying to go with a slightly-dark background with dark text, so it won’t be white, but I won’t have a night/day pair of values to deal with. That’s always a challenge, because for color contrast you can’t go too close to a middle-brightness or you paint away too many values to be functional.

But yeah, the placeholder thumbnail is something I want because many of my oldest posts never had what’s sometimes called a post thumbnail, sometimes called a featured image. And you’d think that WordPress, out of the box, would have some way to define that fallback. You’d be wrong. It’s something that has to be implemented via a plugin or extra efforts as part of the theme.

I don’t really understand that design philosophy of “the obvious feature is not available except by add-on code,” but just have to deal with it, along with using explicit-css-in-json in theme.json, struggling to decide whether to define things on the elements globally or as the elemental children of a core block, and wondering why adding borders to post thumbnails showed up in the site editor but not on the page. Among other things.

Maybe next week you’ll see this site with a shiny new coat of block theme, maybe it’ll take longer. Only one way to find out.

JSON Schemas and Making a Versatile Application.

Please excuse the big wad of JSON in the middle of the post.

Things have come a long way from the previous post (diehealthy.org: 27 October 2022: “Working on a Local Single Page Application.”) about working on a local file that provides a whole data tracking. While I originally wanted it to be used for games, I’d seen other JSON-schema-based stuff online and decided it could be fully schema driven. While it doesn’t yet edit its own schemas, that’s probably feasible in the long run (if not something I’ll necessarily do).

In order to further refine it toward that schema system, I forked it and made a version to track the movies I’ve watch. The post image is of that version, though they are currently at parity aside from the schema differences.

To give an idea what I mean by schema, here’s some excerpts from the film version:

{
    "name": "filmtracker",
    "defaults": {
        "filters": {
            "recorded": 1,
            "watched": 0
        }
    },
    "props": [
        {
            "name": "name",
            "title": "Name",
            "type": "string",
            "display": "headertext",
            "displaystyle": "italics",
            "sortOn": true,
            "req": true,
            "def": null
        },
        {
            "name": "recorddata",
            "title": "Record data",
            "type": "group",
            "props": [
                {
                    "name": "added",
                    "title": "Added",
                    "hidden": true,
                    "display": "fulldate",
                    "displayprefix": "Added on",
                    "type": "date",
                    "sortOn": true
                },
                {
                    "name": "modified",
                    "title": "Modified",
                    "hidden": true,
                    "display": "fulldate",
                    "displayprefix": "Updated on",
                    "type": "date",
                    "sortOn": true
                }
            ]
        },
        {
            "name": "watchdata",
            "title": "Watch info",
            "type": "group",
            "props": [
                {
                    "name": "recorded",
                    "title": "Recorded",
                    "type": "bool",
                    "display": "iffalse",
                    "displayvalue": "Unrecorded.",
                    "summaryvalue": "📅",
                    "filter": true,
                    "def": 0
                },
                {
                    "name": "watched",
                    "title": "Watched",
                    "type": "bool",
                    "display": "text",
                    "displaydepends": [
                        "recorded"
                    ],
                    "displayvalues": [
                        "Unseen",
                        "Seen"
                    ],
                    "summaryvalues": [
                        "💾",
                        "🍿"
                    ],
                    "filter": true,
                    "def": 0
                },
                {
                    "name": "watchdate",
                    "title": "Date of watch",
                    "type": "date",
                    "display": "monthdate",
                    "displaydepends": [
                        "watched"
                    ],
                    "displayprefix": "Watched",
                    "sortOn": true,
                    "sortDep": "bought",
                    "def": ""
                }
            ]
        },
        {
            "name": "filmdata",
            "title": "Film info",
            "type": "group",
            "props": [
                {
                    "name": "releaseyear",
                    "title": "Year of Release",
                    "type": "num",
                    "display": "text",
                    "displayprefix": "Year:",
                    "sortOn": true,
                    "minv": 1900,
                    "def": 2022
                },
                {
                    "name": "score",
                    "title": "Review score",
                    "type": "range",
                    "display": "stars",
                    "displaydepends": [
                        "watched"
                    ],
                    "displayprefix": "Score:",
                    "summaryvalue": "stars",
                    "sortOn": true,
                    "sortDep": "watched",
                    "minv": 0,
                    "maxv": 5,
                    "step": 1,
                    "def": 0
                }
            ]
        },
        {
            "name": "categories",
            "title": "Tags",
            "type": "strarray",
            "display": "ulist",
            "def": null
        },
        {
            "name": "notes",
            "title": "Notes",
            "type": "text",
            "display": "longtext",
            "summary": "📓",
            "def": null
        }
    ]
}

It’s quite a lot, and messy (as is the rest of the application so far), and it mixes data definitions with their presentation, but it’s mostly serviceable. But it lets me define, with the current version:

  • groups
  • numeric inputs
  • string inputs
  • url inputs (unvalidated at the moment)
  • date inputs (though I’d prefer to move to month/year or such)
  • boolean inputs (implemented as radio buttons)
  • tri inputs (radio buttons)
  • range inputs (radio buttons)
  • text inputs (text boxes)
  • string array inputs (for tags, with autocomplete!)

I’d originally gone with checkboxes and three-state checkboxes for booleans and tris, but decided it was too much trouble and having separate buttons looked nicer. The basic checkboxes were reused instead for toggling off or on the listing filters for boolean and tri fields.

It can also search in and sort the listing.


One of the nicer parts of all this is the ability to export and import from JSON, as well as being able to use the browser console to loop through and batch-modify the data. I’m not sure how feasible it would be to implement that through UI, which would be more useful to nontechnical users. But for now this is just a little project to handle my own data.

If I can get it cleaned up, I’ll probably throw a copy up here at some point, though so far I’m still iterating on it a good bit as I go through my old list of movies I watched and fill in their details. I started keeping a list at some point last year, and that was title-only, so it’s been a lot of searching, figuring out which was the movie I actually watched, trying to remember enough to give it a rating.

But the main goal is that if I decide I need a list of something, I can copy the file over, throw a schema together, and start making the list in a way that lets me update it or review it better than a text file and not requiring the ugliness of a spreadsheet. Like when I kept a list of the legislators who objected on 6 January 2021, that’s just a text file. Next time it could be a nice little application that makes the task not necessarily easier, but more useful:

  1. Get a text list of the things
  2. Some light massaging to turn it into JSON
  3. Write a quick schema
  4. Import the data
  5. Fill in any details, be able to filter, sort, search easily.

I can only imagine what data wrangling looks like in 100 years. I hope by then it’s all AI user interfaces that look pretty are functional no friction. Until then, this project has reminded me how powerful, how versatile, and above all how fairly simple, HTML plus Javascript has gotten.

Working on a Local Single Page Application.

If only my editor’s highlighter supported template literals.

I’ll surely post about it more detail as I get it fully built, but I thought I’d write about it as I’ve been working on it.

As I’ve written before (diehealthy.org: 19 September 2020: “How I Track Games to Buy”) about how I track games to buy, using bookmarks, it occurred to me that I’d like something a little more defined than using bookmark titles to store data. And when I say a little, I mean that. I don’t want a relational database (though there is one built in the browser, if I want to use it). I don’t want a server to configure.

I do want a simple web application, often called a SPASingle-Page Application (Wikipedia: “Single-page application”). But as I said, no server. That makes it an LSPA, or Local Single-Page Application. And single-page really means single file, as in one HTML document that contains all the markup, all the code, and all the styles in one package.

The secret of the modern browser is that it has a ton of functionality that it doesn’t get credit for. While (unfortunately) the behavior of localStorage in the file: schema is undefined, at present Firefox makes it a per-file access, so as long as you persist the filename and path, you get the storage back. To be a little more sure of things, you can export the JSON data as a file, and import it from a file.


I’ve used one-off HTML files for other projects before, including years and years ago for some of my Computer Science classes where choice-of-language was wide-open, but it’s been awhile. In general, the browser is a nice platform to write for, but it’s underdeveloped in terms of making these kind of one-file applications widespread. To be fair, there are concerns about users downloading random HTML files and opening up vulnerabilities, but the general shape of browser security seems to guard decently against it such that enabling more local, serverless, in-browser applications would be useful.

People use spreadsheets for all sorts of data storage and simple applications because it’s got all those tools. They could be doing basically the same thing with a browser. (That’s in fact what I am doing with a browser.) In some cases, the numerical prowess of a spreadsheet will make their task easier. In other cases, the web-awareness of the browser makes my task a lot easier.

One place where a spreadsheets take the one-file ideal slightly further: they store the data in the application. Fair enough.


I looked at various libraries to bootstrap building the editing side of things from a JSON schema. There are a bunch of them, but none seemed very easy to integrate or to do what I wanted with it. It took me less time to build the equivalent for my own purposes than I spent looking at and trying to understand the umpteen JSON-to-forms Javascript libraries. And for mine I don’t add dependencies like underscore.js or jQuery.

On the other hand, I’ve spent a bit of my times dusting my ability to write Javascript, wondering what’s canonical these days. There are proper classes with constructors now (but you don’t have to use them). There are things like Map()s that are better than plain objects in some ways, but aren’t as nice to use in other ways.

To save a file, you have to:

  1. Create an anchor (A).
  2. Create a Blob.
  3. Create an object URL for the Blob.
  4. Add the URL as the anchor’s href attribute.
  5. Add the desired filename as the anchor’s download attribute.
  6. Add the anchor to the document.
  7. Call click() on the anchor (the actual download occurs).
  8. Clean up.

Seems like a lot of extra work for a very usual thing. (A roughly similar process to load from a file, except using an input with type of "file" and some other specifics.)


Anyhow, the one feature I’m relying on an extension for that Bookmarks have out of the box is the ability to get the title and URL in a single action. Mozilla Addons: Hiroaki Nakamura: “Format Link” is an add-on I already use to do that for other cases. But it seems like it’s something browsers should support, given how much we all use the web. We still need computers that understand our most-used forms of data as logical objects, but until then there’s nice extensions to help us.

With that ability, the main pieces of data for tracking a game are available with a paste, which isn’t too much more than simply adding a bookmark. The rest of the data was already stuff I was filling in by hand, but it will soon be into my application rather than cramming it all in the bookmark’s title.

Anything else you’d want from a server-provided service can be built locally and only using Javascript. Given I don’t expect to have hundreds of thousands of games to track, I don’t even need to use a relational database. The browser can handle filtering, sorting, search.

For heavier uses, like media databases, solely relying on a LSPA might not be enough power or might not be able to handle some things like creating thumbnails, but for many other uses, it’s a powerful model that I’d like to see more support and frameworks for people to make use of, especially non-programmers or people with only a little knowledge.