About
I think progressive web apps are cool and have a lot of potential, even if platform owners don't provide much support. Still, I wanted a simple resource that would examine a website to see if it was a PWA and show the data that's configured, as well as basics on how to install PWAs and what kind of real support exists for them across various platforms.
I also wanted to do this making heavy use of AI coding tools, as a way to try it out on something simple and innocuous (as I try to process the stark realization of the sea change happening in computer programming). There are notes on that below if you are interested (honestly, I find "golly gee whiz look at what Claude Code did!" blog posts super tedious, but in case you want my thoughts, they are below).
Of note, the content on this site is all authored by me by hand. The installation info is as up-to-date as I can make it - there is a lot of misinformation out there, but I tried these on as many platforms as I could. Do note that for Linux, there could be a lot more ways to set up a PWA than are listed. Further, my PWA support page is highly opinionated, but I think it paints a clear picture of just how much more work platforms need to do to support PWAs. Sweet solution, indeed!
Changelog
- - Changelog, Privacy notes, Refined technical information, Slight UX changes
- - Initial Release
Privacy
This site tracks traffic via Plausible.io, which does not use cookies. A JavaScript widget on this page sends information to Plausible. See their privacy and data policy pages.
This site also uses CORSPROXY to make requests. See their privacy policy (though it seems to apply mostly to me as the customer and not you as the person using a website that sends traffic to it).
Practically speaking, I can see what sites are being entered into the Check URL form, but can't associate them with a person or other identifier.
Technical Notes
To fetch the websites and manifests, this app uses CORSPROXY. Occasionally, some sites that are PWAs (Google Maps is a good example) will result in an error. CORSPROXY also seems buggy at times and doesn't always return a result.
To deal with this, the site first check /manifest.json and site.webmanifest relative to the URL you give me, as those are often where the Web Manifest is stored (and this doesn't seem to run afoul of various blockers websites set up).
Only if those URLs don't respond will this site fetch the given URL to try to locate a Web Manifest from the site's <head>, and then fetch the Web Manifest it finds referenced there.
Golly Gee Whiz, Look at What Claude Code Can Do Everyone! ✨
As I mentioned, I heavily used Claude Code to create this site. It wrote the vast majority of the code, CSS, and markup. I wrote all the content.
It is odd to have deployed a piece of software that I "wrote" but that I don't really know very well, since I only generated the code and didn't write it by hand. The CLAUDE.md file is below, and following that are some experience reports.
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What This Repo Is
pwa.support is a web-app that allows a visitor to paste a URL and get back information about the PWA/Web Manifest support at that site. This will allow the visitor to:
- Sanity check sites they are building to see if they are correct according to a spec
- Provide help in setting up an existing public site as a PWA on the visitor's system
Architecture
- Ignore
Dockerfile.dx,docker-compose.dx.yml, and all ofdx/— irrelevant to this project - The front-end should use HTML Web Components AKA Custom Elements, and should avoid any "loose" JavaScript that runs on load and finds elements
- There should be no back-end; this should all run in the browser where possible
- Avoid third-party dependencies unless there is no other alternative
Directory Structure
bin/— Dev scripts to maintain the appdata/— JSON files used to provide data for.html.ejsfiles, named for their corresponding HTML filesrc/css/styles.css— All CSS for the appsrc/js/config.js— Dynamic configuration injected into the front-end; managed bybin/build, never edited directlysrc/js/pages/— JS entry points for each page, named for their HTML file (e.g.pwa-support.jsforpwa-support.html)src/js/custom-elements/— All custom elements, one per file, named for their tag (e.g.pwa-manifest-checker.js)tests/— Playwright tests, named for the page they test
Process
- Changes to
src/jsshould have tests - Changes across all or most HTML files should have checks added/updated to
bin/check-html - No work is complete until
bin/ciruns successfully
HTML
- Use semantic elements as much as possible
- Use ARIA attributes where needed, but not if a semantic tag already communicates the accessibility information
- Instead of
<div class="...">for styling, consider using a custom element — e.g.<pwa-manifest-property-key>instead of<div class="manifest-property-key"> - If markup is needed purely to establish a block or styling context, a
<div>or<span>may be used
CSS
- Avoid content in CSS
- Use modern features such as nesting and other Baseline features
- If an element can be targeted by nesting without a class, do that
- If an element can be targeted by a semantic attribute without a class, do that
JavaScript
- Three types of
.jsfiles: page (inpages/), custom element (incustom-elements/), and utility (directly insrc/js/) - Each HTML file should use importmaps to map the custom elements or utilities needed, then a single
<script type="module">for its page file - Prefer classes over functions as a unit of code management
- Avoid content in JavaScript
- Private members are preferred over underscore-prefixed names
- Variable names should not use abbreviations
- No semicolons unless required
- Prefer double quotes for strings
Custom Elements
- Each custom element must define
static tagName = "..."and use it when callingcustomElements.define() - Prefer Light DOM; avoid Shadow DOM
- Avoid HTML generation — decorate standard HTML instead
- Prefer attributes for state/status over classes
- For event callbacks, prefer private attribute functions over methods — pass them directly to
addEventListenerrather than wrapping in an arrow function
Notes
- Do not add Co-Authored-By trailers to git commits
- Do not commit code ever unless explicitly requested to.
My Intrinsic Motivation was Only On Outcomes
The entire experience felt like project or product managing at a very fine level of detail. I almost never made any substantive changes myself. I tried to ask it to do everything. In a few cases it was easier to make a small tweak than find a way to phrase the request.
I did ask for developer-focused things like tests and other checks, but whenever I thought about this and thought about building it, I was almost entirely focused on how it worked or what it did.
The Code Feels Messy, but I Can't Really Explain Why That Matters
This is definitely not how I would've structured this project, but it's hard to argue with what it did, since it seems capable of making whatever changes I ask for. As an example, there are several HTML files, each having a ton of duplication (e.g. in the head). Normally, I'd factor that out, but since the AI can correctly make changes across all the files, there doesn't seem much point in creating an abstraction that's just for me.
The site's main nav "should" be a parameterized include, but it's just duplicated. Whenever I added or changed pages, the AI updated all the copies. I eventually asked it to write a test that the navs were indeed the same. It's hard to argue DRY applies here.
The JavaScript code itself was not consistent nor well-organized. Again, I don't think it matters that much, but even now, looking at it to write this, I see things that I just can't abide existing, so I did ask it for some refactoring that, if I'm being honest, was not necessary and just to make me feel better. This sloppiness in now way affected my ability to get it to make changes. But, this is a tiny very simple site.
This App is So Simple, It's Hard to Draw a Conclusion
This app is extremely simple. There's no back-end. It's almost entirely text. So the ease with which it was produced is not, at least in my mind, an indicator of the general applicability of AI coding tools.
I Must Admit It Was Mostly Fun
There was some frustration at first, but generally, I enjoyed the process of using the AI to generate the code for this app. I really did like not having to think about styling, JS, HTML, etc. and just focusing on what I wanted it to do. It felt like a lot of friction was removed.
I chose this style of app as a test because I really don't enjoy UI design or front-end coding and I despise Playwright. This app is so small and niche that it would make no sense to hire a professional to do it. I'm sure a professional would do a better job at UI design, but I just wanted the app to exist and not totally suck. I think I've achieved that.
I do plan to use this style of coding on something more substantial to see how it works. While I can tolerate messy front-end JavaScript (to a point), an un-normalized database filled with nullable fields is right out and I won't stand for it. At least that's what I'm telling myself.
Version: cb347bc0f69878371bee8ac72f8887df623e6ad2