If you’re unfamiliar with Jazzy Generator Tag, it’s a silly little plugin I wrote a couple of years ago to zhuzh up the generator tag that gets added to the HTML source advertising the installed version of WordPress.
The plugin filters the standard “WordPress 5.6” (or whatever string) to include the jazzer for the currently installed version.
For WordPress 5.6, the jazzer was Nina Simone. So the generator tag running on WerdsWords right now reads, “WordPress 5.6.1 to the sounds of Nina Simone.” Go ahead, check out the source and take a look!
What you see is what you can extend in APIs is the mantra I live by while writing my own APIs and when evaluating others’.
In my career as a web developer, I’ve interacted with probably hundreds of APIs, even written a fair few. Through all of that experience, I’ve come to one very specific conclusion about what constitutes “good” even “delightful” API design: Public access is key.
Public vs private APIs
The one marker, for me at least, that makes an API stand out is just how much the actual API code can be extended. That is, how much of the API is public and how much is private? And how much of the public vs private functionality is the API itself using?
Now, obviously, this discussion is heavily focused on FOSS APIs. This is largely because you can literally see and interact with the internals.
When examining public vs private, often the best approach is to consider the rules of the API itself. What constitutes “extendable” and what doesn’t? Does the core use a bunch of stuff hidden behind barriers or can you extend the same code core does? What is the core’s policy on backward compatibility?
Once you’ve set a public vs private baseline, then deciding openness is usually pretty simple. I’ve always felt that open and public should nearly always be preferred. It almost feels a little bit lazy for an API to bake in special goodies for itself then mark it private like it’s unfinished or something.
Examples in practice
We’re right in the middle of a big project at Sandhills right now. We’re building a new pro add-on for AffiliateWP called Affiliate Dashboard. It introduces a portal-like experience for affiliates, largely intended as a premium replacement for the standard core affiliate area.
The standard affiliate area API is template-based. Themes can override core templates, even introduce custom ones. It’s a very free and open extension environment but also very difficult to enforce high standards in the long term.
On the other hand, Affiliate Dashboard API is control-based. This means first- and third-party developers register display elements through the API and the add-on itself controls rendering them.
A dynamically-built Statistics view in Affiliate Dashboard.
At the time of writing, approximately 95% of the add-on dogfoods its own public APIs to render itself. We register views, sections, and controls the exact same way any other developer would. This is in part to prove a point that amazing things are possible, and in part to provide a living example for best practice.
For instance, want to see how we did something, maybe make your own version? The add-on source code is your best, first reference.
To be fair, there’s still a very small percentage of functionality we haven’t yet converted from its MVP form. By the time we ship the 1.0.0 stable in a few weeks, we’ll be entirely rendering on our own public APIs. Pretty sweet!
Delight and deliver
I like to think that we’ve designed the Affiliate Dashboard APIs with an intent to delight and deliver. Within the boundaries of what the API itself allows, what you can see is what you can extend.
Moreover, we’ve gone to great pains to make it feel consistent from control to control. For instance, some controls output very simple things, others do incredibly complex things under the hood like rendering a table using REST and Alpine JS.
The main point is that in trying to delight our extension community, we’ve forced ourselves to think simply when it comes to writing extensions:
All controls have a similar entry point
All controls use the same structure of attributes and arguments
Pretty much all controls support Alpine JS directives
All controls support a limited set of TailwindCSS classes
This consistency begs the question, “What is the fastest way to get developers from A to B and promise a really high quality result?
After some reflection, I think it’s to write a whole lot of excellent example code that oh hey, by the way, happens to run the add-on itself. Surprise!
Joe Dolson, one of the WordPress Accessibility team reps posted a letter on make/accessibility this afternoon that really struck a chord with me. In detail, the letter outlines the Accessibility team's perceived shortcomings of Gutenberg. the new block editor set to ship in a few weeks with WordPress 5.0.
In reading the letter, I was struck by a key theme that Joe so eloquently expresses: making something technically accessible doesn't automatically make it a good experience for the users it serves to assist.
He went on to detail several issues, but the one that really stuck out to me had to do with using keyboard navigation to access a block's settings to change the font size of some selected text (emphasis his):
1. Press Ctrl + `four times to locate the block settings.
2. Press tabfive times to reach the font size selector. Discover the usage of the non-standard selector dropdown (normal selector: arrow key down to desired value, press enter to select, tab through rest of document. This selector: Enter to expand dropdown, tab key to choose desired value, Enter to select that value, esc key twice to exit selector.)
3. Press tabsix times to locate skip link back to selected block.
4. Press Enter to activate the selected block.
5. Press tabthirteen times to reach the editable text of the block.
The above navigation scheme required 34 separate keyboard stops in order to change the font size of the selected text and return to the previous position, and is aided in efficiency by the tester’s prior knowledge of how to navigate the process. (Tested in Chrome and in Firefox using NVDA.)
We want to be clear that the above example is not comparable to the options available in the classic editor – there is no mechanism for increasing the font size of a paragraph in the existing editor.
Even with the final concession that there is no comparable feature for changing the font size of a paragraph in the classic editor, I'm not sure this is considered an improvement. Maybe for users who don't have to do it with a keyboard? Yikes.
As a core developer, I'll admit that I've been relatively silent on Gutenberg and the 5.0 release until now.
I don't hate Gutenberg. In fact, the idea of Gutenberg is awesome, even inspiring. This post was written using Gutenberg. It represents the opportunity for a giant leap forward for content authoring in WordPress, and frankly I don't think anybody really disagrees with that assertion when it's just an idea.
When Gutenberg becomes more than an idea, however, when it's real and out there in world, that means something to a lot of people who look to WordPress to set the example. It sends a powerful message to 32% of the web: "this is the new standard."
Please let's not make the "new standard" be that we're willing to ship technically accessible but perhaps not entirely usable-for-all features; let's not define it as one that sacrifices standards core to the WordPress experience in the name of perceived expediency; let's not define it as the new default authoring experience for all users when not all users can use it well.
The WordPress philosophy states deadlines are not arbitrary. That's fair, that's something we live by. Core standards are not arbitrary either, and accessibility is a not a one-more feature.