Table of Contents
Open Table of Contents
Who This Post Is For
This post is a collection of pitfalls I ran into developing progressive web apps or PWAs (such as WeightBook) without testing on actual devices, especially iPhones. Looking at your app in the browser dev tools or an Android test device can only take you so far. If you are interested in hosting your PWA using nginx and kubernetes / docker, also have a look at Build a progressive web app in docker with nginx to deploy to kubernetes or docker swarm.
This is a living collection and might be sporadically updated when I encounter new issues ;).
Issues
1. Users Can Highlight All Text
By default, users can highlight text everywhere, for example, by long-pressing. This will show a context menu or auto-translate the highlighted text in an overlay.
To fix
Create a CSS class that prevents selecting text and apply it to any element that you do not want users to select text in. This class can be applied to UI elements where the user should not be able to highlight text, for example buttons or cards. If lazy, just apply it to body
but be aware that it will disable text selection everywhere and be bad UX ;). See https://www.w3schools.com/howto/howto_css_disable_text_selection.asp.
.prevent-select {
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
user-select: none; /* Standard syntax */
}
2. Transparency in PWA Icons
Do not rely on a consistent background color for your PWA icons with transparent backgrounds. The initial icon for WeightBook was a black outline of a sheet of paper. It looked good in my testing and in light mode, but when users with dark mode installed it on their devices, the icon was just black.
To fix
Make sure the icons in your Web app manifest work on different backgrounds, especially in light and dark modes.
3. Background Scrolling on iOS
When scrolling to the edges, iOS adds an elastic bounce effect to even installed PWAs.
To fix
To remove this you can set a fixed body position on the body
element:
body {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
touch-action: none;
}
(from Stack Overflow)
4. Notches Overlap Content
Camera lenses, top notches or (on iOS) overlays at the bottom can overlay the viewport of your PWA.
To fix
You can use safe-area-inset-*
in your CSS to avoid those (see https://developer.mozilla.org/en-US/docs/Web/CSS/env#values). For example, add a safety padding around your content:
body {
padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom,
0px) env(safe-area-inset-left, 0px);
}
Or create a CSS class to apply to elements that need additional padding:
.padding-bottom-inset {
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 1rem);
}
To use these values, make sure to set the correct viewport
meta tag, especially viewport-fit=cover
:
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover"
/>
5. Test Features That Require HTTPS Locally
Some features of your app (such as installing it ;)) might not work on emulators or your own devices if your app is served from HTTP origins, such as localhost
.
To fix
For Vite, you can add @vitejs/plugin-basic-ssl
to your project. A minimal plugin configuration to add to your defineConfig
is:
export default defineConfig({
plugins: [
basicSsl({
name: 'dev-text',
}),
],
// ... other configs
});
6. Android Appends .txt to Files With Unknown Mime Type
If you download a file with a mime type unknown to Android, it appends .txt. For example, a SQLite database export called weights.db
becomes a text file named weights.db.txt
.
To fix
Look up a fitting mime type on https://mimetype.io. If the mime-type you are offering does not work, check if it is correct (e.g., https://mimetype.io/application/vnd.sqlite3 shows “This mimetype is deprecated” for SQLite databases).
Assign a mime type to a file and offer it for download using (for example with application/x-sqlite3
):
const fileToExport = // create the file ...
const file = new Blob([fileToExport], {
type: `application/x-sqlite3`,
});
const fileUrl = URL.createObjectURL(file);
const a = document.createElement('a');
a.href = fileUrl;
a.download = 'weights.db';
a.click();
a.remove();
URL.revokeObjectURL(fileUrl);
7. Avoid Pull-To-Refresh
When dragging down, iOS triggers a pull-to-refresh.
To fix
Set overflow hidden on the body and show content in an element with overflow-y: scroll;
and a fixed height:
body {
overflow: hidden;
}
.page {
height: 88dvh;
overflow-y: scroll;
}
(from Stack Overflow)
In Summary
I can’t speak from recent experience, but I’ve previously used the Ionic Framework to reduce the mobile specific issues I’ve encountered and found it good to work with. So probably, do not do this yourself but work with a framework.