Design Patterns You Can Try Yourself Online

Last updated:

Web Apps are bad. A big part of that has to do with the experience of using them, which is harmed by stakeholders trying to fit round verticals into square products. I’ve spent my entire life online, and in so living have considered a few design patterns that I haven’t seen but maybe could, in a more perfect world.

I use these patterns where it makes sense to, and here share them in the hopes that maybe you’ll find them useful or iterate on them yourself 1.

Saving, Loading, and Storing Work Files

Screenshot of a tweet asking why Excel's save icon represents a vending machine
There is a sense of tension when I’m organizing documents in “the cloud.” This may be my use case speaking, but I often have to refer to documents from years ago while I’m working on writing things for my day-to-day work, and a “Recent docs” feature doesn’t really cut it for me. I don’t want to deal with some half-baked file organization implementation (Google Drive) when I have a perfectly good Finder right there.

I have mixed feelings about cloud storage conceptually. The persistence is convenient, the fact that I don’t have to think actively about saving can be nice (except in situations where saving provides a useful “checkpoint”). But I don’t like being at the mercy of an organizational system that isn’t mine, that isn’t even very good2, not to mention having a megacorporation breathing down your neck that can decide to delete your documents at any time or take away access as a consequence for participating in their own system.

One solution is for Users to bring their own cloud storage provider, and plug that into a document editor frontend, but in practice this only kind of works. Most Users will just go with whatever system is the default, and building support for multiple file store backends seems onerous given that terms can change at any time. The closest real-world example I can think of would be something like Obsidian with self-hosted Nextcloud sync, which works like a gem but is unfortunately more trouble than it’s worth for most people.

While working on Substrate, a mind-mapping and ideation webapp, I decided to try and aim for the best of both worlds by using IndexedDB to keep track of the information in a document. You can also export or import the document as a file to take it to other computers or back it up however you like3.

Here’s the actual pattern: You can append arbitrary paths at the end of the app URL to create and organize documents. For example:

URL Purpose
[URL]/ This is the “default” document: could be an introduction screen or onboarding experience, or just another blank document
[URL]/vehicles A document about vehicles
[URL]/vechicles/planes A document about planes. Note that the document doesn’t inherit anything from following “/vehicles”, it’s just an organizational affordance
[URL]/vehicles/trains A document about trains

And so on.

So we’re kind of inverting the URL’s relationship to the server and client. Instead of reflecting a path on the server, the URL reflects a path…in your mind?

The URL is the path hierarchy which IDs documents that are stored locally on your computer. From the User perspective, the interaction is very similar to cloud-based storage but you actually keep your data locally. Organization is as simple as choosing where you want to put the document, and the browser’s own bookmark and pinned-tab affordances become your file manager for free. For the lightweight apps this pattern is most suited to, such as notes and to-do lists, bookmarks are sufficient.

The pattern would benefit from an affordance to make Users aware of siblings/children/parents of the current document, which would make it easier to navigate the org hierarchy. This is where my usage of IndexedDB comes back to bite me because I don’t think I’m able to support this interaction due to privacy protections. Maybe you’ll come up with a clever solution.

Iconography

How best to differentiate automatic document saves from manually saving the file to your computer? NN/g did some great research into save icon recognizability and concluded that more Users recognize the floppy disk icon as “save” than downward arrow to container, which is usually read as “download.”

In this case, I think “download/upload” is a more meaningful label for this manual save interactions than simply “save,” because saving happens automatically and constantly. Use circular arrows when auto-saving, and downward/upward arrows to containers for export/import.
Table comparing icon recognizability: floppy disk recognized as Save by 83% of participants, circular arrows recognized as Sync/Refresh by 73%, and a downward arrow into a container recognized as Download or Save by 78%
For Substrate, my quick-and-dirty unicode implementation resulted in this floating action bar:
Substrate's floating file I/O toolbar showing four text-based controls: 'save' with a down arrow, 'load' with an up arrow, 'clear' in red, and a question mark for help
More successful labels might be “download” and “upload,” however I have not done any A/B testing to that effect. I’m also using ? for help where a circle-i with a text label may be more appropriate.

Downsides

This pattern balances tradeoffs between 100% cloud-storage on the web (e.g. Google suite), and 100% PWA using local documents (e.g. Photopea). As such, it comes with its own set of pushes and pulls:

If the User clears their browser data, all their un-downloaded files are zapped. This will confuse people, as evidenced by the history of HTML5 game save files.

Cloud storage allows you to pick up right where you left off on another computer, with access mediated by an account system. This pattern would require Users to manually download and manage their own document files — I think they ought to be doing that anyway, but that’s a different story.

Translating a file to another url is kind of a chore. The User needs to export the document, clear the workspace, change URL, and then import the document they exported. That’s a lot of steps for a simple move procedure.

So Why Not Make a PWA?

The dream would be some way to load a file into a browser, and modify it in place PWA-style. But that’s a pretty huge security nightmare. This is anecdotal, I’ve never seen a User install a PWA in the wild, including those weird little schedule webapps at conferences. This pattern is meant to exist in a sort of sweet spot in between PWAs and websites: a way to get some of the benefits of a PWA while still existing on the web.

LLM Inference: Agency & Opt-In

I’ve been playing around this Summer with experiments involving UI for local, on-device LM inference. It hopefully goes without saying that an LM shouldn’t be the product. A tool needs to work perfectly well driven by a human without any LM features, otherwise it’s likely some sort of toy or demo. This fuels my opinion that LM features, if they exist, should be opt-in and off by default. The story of software isn’t the tech, rather what the User can do with it.

The iconography associated with LM-powered features should be chosen deliberately to communicate the feature to the User — not to highlight the tech that powers it. Sparkles imply “magic,” “effortlessness,” “cutesy,” “delight.” LMs aren’t cute4. I’ll leave scoring effort and delight as an exercise for the reader. Pretending machine learning is magic is how you get people making Midjourney medbed spas.

I think interfaces should be honest. They should be intuitive and clear about their function, and they should be honest about what those functions are. All software uses metaphors, like desktops or folders or dragging or dropping, to make deterministic operations legible to human Users.

Generally, software does exactly what is instructed, in exactly the way it has been instructed to do the task. The whole value proposition of agentic LMs is that they don’t work this way5: they observe, orient, decide, and act in a way that’s opaque and probabilistic. The system is observing you, and it chooses an action (within its affordances), not the other way around.

The best case scenarios I’ve found where language models are actually useful is as a small bit of fuzzy logic in an otherwise deterministic pipeline. For example, I’ve used a model call in a Python script to choose from a list of mutations we might want to do to a line of Markdown based on the semantic content of the line as part of a document prep task involving a pretty huge batch of text files. You might have a system monitoring changes to a document and firing model calls in response to a user’s choices, or interaction temperature over time.

So how do we communicate that the system is monitoring what the user is doing? How do we allow the user to opt-in, and make it clear when the system is or isn’t loading or sending calls to a model?

I propose 👁.

An eye implies, “watching,” “paying attention,” “reading,” and also “surveillance,” “being watched,” “scopophobia.” One of many key messages should be that you(r actions) are being monitored. You also don’t know what’s going on behind an eye. A little intentional psychological friction can be a great way to shape the perception of a tool, and that starts with the affordances and iconography a system uses to represent itself.

Here’s how it looks in Substrate’s floating toolbar when the suggestion agent is active:
Small UI control showing a black eye icon next to a horizontal slider labeled 'quiet' on the left and 'loud' on the right

And inactive:
Small UI control showing a closed eye icon next to a horizontal volume slider

Notice that in addition to the eye, there’s a slider for “volume.” Different people seem to prefer different verbosity and suggestion frequencies for LMs. Volume abstracts that preference into an intuitive macro control. At low volume the system stays quiet unless explicitly invoked, using controls that only appear when the agent is turned on. At medium or high, it surfaces suggestions proactively.

LM tech is overhyped, poorly understood by the stakeholders mandating it, and shoved in places it doesn’t make sense to be. Most of the time it’s a grift upsell disguised as a value-add, but maybe post-bubble things will be a little more normal. The least we can do as designers is try to be a little less credulous about how we communicate technology to Users. If I put a three sparkles icon into a product please shoot me in the brain.


  1. If you do choose to iterate on one of these, or if there are other designers exploring similar options, I’d love to hear! Email me@ellie.online.  

  2. If you’ve ever tried to organize a large number of files in Google Drive, you know what I mean. Even Windows Explorer provides a more snappy experience with fewer inexplicable design decisions.  

  3. I think YAML is best for text-based export, or possibly JSON. YAML is extremely human-readable and has minimal additional markup. JSON is a little more markup but is similarly readable, for the most part. But the crucial thing is that if a save file is text-based, you can git track it. I’m not sure what the best option is for documents that include images. Base64 works, but sucks to deal with in the file. For the love of god do not invent a new structured document syntax: using an existing format means you can work with existing tooling, and other people extending your thing don’t have to build a save/load pipeline which will just turn it into a structured format anyway. 

  4. Chatbot cuteness peaked with my 2016 piece Emma Can Learn. I can’t support this empirically, but I believe it in my heart. 

  5. This isn’t to say that this behavior is generally a good thing, but that that’s where the value proposition lies.