Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mvneerven/pure-pwa
Ultra-lean, Web Components enhanced, no-build, no-dependencies boilerplate for PWAs
https://github.com/mvneerven/pure-pwa
html-css-javascript no-build no-dependencies no-framework progressive-web-app pwa webdevelopment
Last synced: 6 minutes ago
JSON representation
Ultra-lean, Web Components enhanced, no-build, no-dependencies boilerplate for PWAs
- Host: GitHub
- URL: https://github.com/mvneerven/pure-pwa
- Owner: mvneerven
- Created: 2024-01-29T21:14:27.000Z (10 months ago)
- Default Branch: master
- Last Pushed: 2024-03-07T07:37:34.000Z (8 months ago)
- Last Synced: 2024-11-07T21:12:47.247Z (7 days ago)
- Topics: html-css-javascript, no-build, no-dependencies, no-framework, progressive-web-app, pwa, webdevelopment
- Language: JavaScript
- Homepage: https://www.pure-pwa.com/
- Size: 1.03 MB
- Stars: 285
- Watchers: 11
- Forks: 18
- Open Issues: 5
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# PurePWA - Power & Purity
> PurePWA is a **Radical Experiment** in Simplifying Boilerplate for PWAs, in which we force ourselves to only use the **Modern Web** standards, and nothing else.
![mobile-view.webp](./md/mobile-view.webp)
# Table of Contents
- [Setup](#Setup)
- [Project Structure](#Project_Structure)
- [Usage](#usage)
- [New in](#New_In)
- [More Info](#more_info)# Setup
> There is no setup.
💡 This is a vanilla, accessible, SEO-friendly, ultra-lean, no-build, no dependencies `PWA` project, using only what's included in the Web Standards and has been implemented by Modern Browsers.
The two most important technologies used are:
- Web Components
- ES Modules (loaded and linked at runtime)## Tooling
I have developed the code using these tools:
- Microsoft VSCode
- VSCode extensions:
- ritwickdey.LiveServer (Easy to use built-in web server)
- glenn2223.live-sass (Live SCSS compilation)
- PWABuilder.pwa-studio (optional - Loads of PWA goodies)Using the tools mentioned above, the PWA is directly served using `LiveServer`, and CSS is compiled from `.scss` files by `live-sass`.
**Nothing else is needed.**
# Project Structure
![Folder Structure](./md/project-structure.webp)
## Web Root: `public/`
This is a `hybrid MPA`, which means that we have multiple app pages, each one in its own folder.
This provides a number of undeniable benefits:
- No need for SPA routing 🤯
- Code readability and maintenance
- Smaller, optimized downloads,Although base routing is handled by the web server, each route can still setup sub-routes and handle them, through a web standards-based routing mechanism ([Navigator.navigate](https://developer.mozilla.org/en-US/docs/Web/API/Navigation/navigate)).
The `public/` folder is the one getting served as root (see `.vscode/settings.json`).
Within `public/`, there's one system folder (`assets/`), and a folder for each `MPA` page.
![App folder example](./md/public-folder.webp)
### Assets
The `assets/` folder holds shared images, css (compiled from `/scss/app.scss` and its `@import`-ed `.scss` files).
![App folder example](./md/assets.webp)
The `assets/js/` folder holds:
#### 1. `services`
Service Worker and related
#### 2. `shared`
Web Components (each in a seperate folder)
#### 3. `app.js`
Main entry point
```html
```
#### 4. `common.js`
Shared, exported symbols used elsewhere.
```js
import { CustomElement } from "../assets/js/common.js";
```### MPA Pages
Each MPA Page folder can consist of `HTML`, `SCSS` and `JS` files (and other files that are related to the page).
![App folder example](./md/app-folder.webp)
The `SCSS` files are imported into the `index.scss` file in the public root, and are compiled automatically by the Live Sass Compiler, into `public/assets/css/app.css.
> The '\_' in `_index.scss` means the stylesheet is going to be imported (@import) to a main stylesheet i.e. styles.scss.
This is a choice I made, to have just one CSS file served. Suit yourself if you would like to implement a different approach.
If there is a `.js` file in an app folder (such as in the example), the `index.html` file will include it, like this:
```html
```
The main advantage of this approach is that `MPA` pages are isolated modules, with page-specific behavior just declared for that page.
This means improvement **maintenance**, **readability**, **separation of concerns**, and **deployability**.
## Shared Components
Having separate app pages, in an `MPA` app, doesn't mean that there's no shared code.
### ECMAScript Imports
We rely on the standards-based, `ECMAScript` runtime import mechanism, that is similar to the way imports are done in bundling/building environments (webpack, vite, esbuild, etc.), but uses the `.js` extension:
```js
import { CustomElement } from "../../common.js";
```## Web Components
PurePWA uses two kinds of Web Componenents:
### 1. Self-contained controls (with Shadow DOM)
These components insert their own content into the `Shadow DOM` below their container element. Styling is then isolated and should be done using [adopted stylesheets](https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets).
### 2. Progressive Enhancement wrappers (with Light DOM)
These components enhance existing, contained HTML structures, using the `Light DOM`, the content nested within the Web Component's tags. Styling doesn't differ from standard styling in the rest of the document.
### The CustomElement class
This is a generic Web Component class that offers a few benefits over the `HTMLElement` base class:
#### 1. `state` property (managed by a `Proxy` object)
- [More on the `Proxy` class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
- [Make your own state management with Proxies and Event emitters](https://dev.to/fly/make-your-own-state-management-for-react-with-proxies-1n0m)#### 2. `render()` method that mimics framework best practices, but uses `ES String Literals`
```js
render() {
const builder = new HTMLBuilder(/*html*/ `{html}
`);for (let item of this.listViewItems) {
builder.add(this.getItem(item));
}
return builder.toHTML();
}
```### The RouterElement class
Inherits from `CustomElement`. Provides simple 'SPA'-like routing, but within the `MPA` page.
Instead of the `render()` method of the `CustomElement` class, you use the `routes` property to implement your rendering logic:
Example:
```js
get routes() {
return {
"/movies/": this.getPopularMovies.bind(this),
"/movie/": this.getMovie.bind(this)
};
}async getPopularMovies() {
hookEscapeKey(() => history.back());const movies = await this.tmdb.getPopularMovies();
return /*html*/ `${this.getCards(
movies.results
)}`;
}
```# Native Look & Feel
One of the goals of the `PurePWA` experiment is getting a **near-native** experience. But to get that, we first need to get a grasp of what that actually means.
- Glossy appearance (scalable vector graphics, gradients, shadows)
- Download/launch/interaction speed
- Optimal device real estate usage
- Responsive, mobile-first design
- Offline capability
- No HTML reflows during app usage
- Micro-animations: subtle effects to give immediate feedback, more user satisfaction and engagemen, create fluid state transitions, and even prevent errors (because of immediate feedback).
- No browser chrome (PWAs run in the browser, but you want to hide all browser UI)This project tries to address all of these areas.
## SVG Sprites
For PurePWA, I've chosen the very much undervalued `SVG Sprites` technique for icons and other graphics.
Check out this [free store for more icons](https://materialdesignicons.com/).
```xml
```
The `svg-icon` Web Component is used to include graphics within the HTML structure.
```html
Home
```The `svg-icon` component sits in the `/public/assets/js/shared` folder.
```js
import { CustomElement } from "../../common.js";customElements.define(
"svg-icon",
/**
* Renders SVG icon using SVG sprites defined in /assets/img/icons.svg
*/
class SVGIcon extends CustomElement {
static get observedAttributes() {
return ["icon"];
}attributeChangedCallback(name, oldValue, newValue) {
if (name === "icon" && newValue) {
this.querySelector("use")?.setAttribute(
"href",
"/assets/img/icons.svg#" + newValue
);
}
}render() {
return /*html*/ `
`;
}
}
);
```# Showcased Technologies
## General
- Auto adapting app menu (hamburger, bottom menu on mobile)
- CSS grid auto flow
- SVG sprite icons
- Micro animations (and consideration of user preferences)
- Light & Dark modes (defaults to system settings)
- Automatic caching of MPA pages (to minimize delays/page loads)
- Skeleton Empty States## MPA Demo Pages
On each of the demo pages, there's a lot of stuff going on, but all of it is done with pure HTML, CSS and JS, and without any dependencies.
Here are some highlights:
### Power
- Peer-to-peer video calls using WebRTC API
- Web Share API### Purity
- Accordion using details/summary semantic HTML tags
- Progressive enhancement (accordion-details Web Component)### Action
- Use of `fetch()` API
- In-page router using `Navigator.navigate` interception
- View Transition API: morphing between master & detail
- Custom Skeleton Empty State### Flow
- Semantically correct, accessible web form
- Enclosing label inputs to avoid unique ids and 'for' attributes
- x-form progressive enhancement Web Component### Entry
- Great accessibility
- Semantic HTML (fieldset, legend, label, aria-attributes)
- Progressive enhancement dropdown-list (using input + dynamic datalist)
- CSS-based required field indication
- Powerful HTML5 Form Validation API### Beauty
- Scrollbar Snap Behavior (scroll-snap-\*)
- View Transitions
- SPA sub-routes# Development Experience
Did you notice?
- No `node_modules/`
- No `package.json`
- No build time# Localization
PurePWA supports localization (introduced in version `0.1.0`).
The `/public/assets/js/app-settings.js` file's `APP_SETTINGS.localization` hosts localized strings
```js
localization: {
defaultLanguage: "en",
strings: {
"Power & Purity": {
nl: "Kracht & Puurheid"
},
Power: {
nl: "Kracht"
},
Purity: {
nl: "Puurheid"
},
[...]
```To accomodate for larger, localized sections (which are handy for localized HTML blocks), there's a `` Web Component :
```html
PurePWA is an experiment by
Marc van Neerven, a Fractional CTO with a passion for the Modern Web
The aim was to simplify boilerplate for PWAs, while just using the
Modern Web, and nothing else.
The Modern Web is a beautiful place, but many Web Developers seem to missed
that, with the 'framework path' being what everybody gets taught.
```
The `url` attribute refers to a file under `/public/assets/locale/<2-letter-language-code>/`. You can see a few examples of these HTML block files under `/public/assets/locale/nl/`
## Localization Under the Hood
Since a lot of PurePWA uses Progressive Enhancement, we had to come up with an easy way to localize-in-place. This is quite different from a Framework app that generates UI from code, like React, Angular of Vue.
We use the `Document.createNodeIterator()` method together with a custom Node Filtering to make very efficient `#text` node changes based on our localization strings.
```js
const textFilter = (node) => {
return node.nodeType === Node.TEXT_NODE
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
};const iterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_TEXT,
textFilter
);
let node = iterator.nextNode();while (node) {
// translate
node = iterator.nextNode();
}
```# Releasing a PWA version
## App name, version and file List (ServiceWorker)
Since the PWA has a ServiceWorker that caches the app files, the array of application files needs to be in `service-worker.js`:
```js
const app = {
name: "pure-pwa",
version: "0.1.0",
files: [
"/about/index.html",
"/action/domain/tmdb.js",
"/action/index.html",
"/action/movies-api.js",
"/assets/css/app.css",
"/assets/css/app.css.map",
[...]
```To update this array (if needed), you can run the PowerShell file in the project root:
```powershell
.\update_cache_files.ps1
```Since the project doesn't have bundling and NPM, this was an easy way to get the file list updated.
The .ps1 file updates `/public/assets/js/services/app-files.json`, and before deploying, I manually copy the array contents to the `service-worker.js` `app.files` array.
Before deploying to your cloud location, don't forget to update app.name (first deploy) & app.version (every deploy). These are used in the ServiceWorker code to refresh the cache.
# Stripping Down to Boilerplate
`/public/assets/js/app.js` contains a few lines that add the demo pages and some other demo-related stuff to the project:
```js
import { mergeDeep, PurePWA } from "./common.js";
import { APP_SETTINGS } from "./app-settings.js";
import { DEMO_SETTINGS } from "./demo-settings.js";/**
* TO REMOVE THE DEMO, CHANGE THE NEXT 2 LINES INTO THIS:
* let appSettings = APP_SETTINGS;
*/
const appSettings = {}; // APP_SETTINGS
mergeDeep(appSettings, APP_SETTINGS, DEMO_SETTINGS);window.purePWA = new PurePWA(appSettings); // Launch main PWA controlling component
if (typeof navigator.serviceWorker !== "undefined")
navigator.serviceWorker.register("/assets/js/services/sw.js");
```Creating pure boilerplate thus comes down to changing the code:
```js
import { mergeDeep, PurePWA } from "./common.js";
import { APP_SETTINGS } from "./app-settings.js";window.purePWA = new PurePWA(APP_SETTINGS); // Launch main PWA controlling component
if (typeof navigator.serviceWorker !== "undefined")
navigator.serviceWorker.register("/assets/js/services/sw.js");
```Of course, you'll then remove the `about`, `purity`, `flow`, `entry`, `action` and `power` folders and adapt the `routes` property in `/public/assets/js.app-settings`.
# New In
## 0.1.6
### `CustomElement.on` changes
`on(eventNames, funcOrObject)`
- `eventNames`: Attaches one or more event listeners. Space-separated.
- `funcOrObject`: function, or object with selectors as keys and functions as values.Example:
```js
rendered() {
this.on("mousedown touchstart", {
".next": (e) => {
// do something on button.next click
},
".prev": (e) => {
// do something on button.prev click
}
});
}
```## 0.1.0
- Added a custom Splash screen (all other pages, including `/home/`, are now in their own folder)
- Added support for Localization (see Localization).
- Fixed `` HTML semantics# More Info
If you want to follow further developments of the `PurePWA` project, follow this repo, and these other sources:
## Blogs
- [PurePWA: Power & Purity](https://medium.com/javascript-in-plain-english/embracing-the-beauty-of-the-standards-in-2023-web-development-29dbeece2966)
Follow the [#purepwa](https://www.linkedin.com/feed/hashtag/?keywords=purepwa) hashtag.