Navigate back to the homepage
Browse Repo

Building a Mobile App using HTML, CSS, and JavaScript

Wisdom Ekpotu
May 2nd, 2022 · 5 min read

So you have seen the title, and you are wondering how you can pull off building an actual mobile application by just using the knowledge of basic web technologies without the need to learn Android or IOS development? This is made possible by converting your regular web applications to work as a standard mobile application that can be installed on multiple platforms. By doing this, we will achieve a type of application referred to as Progressive Web Apps (PWAs).

In this article, we will learn how to leverage the power of HTML, CSS, and Javascript to build a simple mobile app. We will not be using frameworks like Ionic or React Native. This is because this tutorial focuses on showing how a basic web app can be made to feel and behave like a native mobile application that can be installed and run on mobile devices using the most straightforward method with the least abstraction.

To proceed, let us have a brief introduction to PWAs.

What are Progressive Web Apps?

According to Official MDN Web Docs:

Progressive Web Apps (PWAs) are web apps that use service workers, manifests, and other web-platform features combined with progressive enhancement to give users an experience on par with native apps.

In simple terms, they are basically websites styled like apps that can be run either inside a website browser or installed directly on a mobile device and accessed like a native app.

There are three key components of a PWA;

  1. Service Worker: The service worker transforms the website into an app by allowing it to download and cache files on a device.
  2. Web Manifest: This JSON file provides the basic meta-information about the app, such as the app icon, background color, etc.
  3. Secure HTTPS: HTTPS is mandatory and makes PWAs more secure than regular web apps.

PWAs have pros and cons. Among the former:

  • Cheap and Fast Development: PWAs are less expensive, quicker, and easier to create than native apps. Native app development from the ground up necessitates particular technologies for both platforms. HTML, CSS, and JavaScript are all that are required for a PWA.
  • Cross-Platform Availability: One of the promising advantages of PWAs is that they can be installed and run on multiple devices across various operating systems.
  • Offline Functionality: Having bad or no internet at all won’t stop users from using your app as it can cache data for offline viewing using service workers.
  • Performance: Compared to native mobile apps, PWAs are much lighter, don’t take as much memory space, and have a faster load time.

On the negative side:

  • High Battery Usage: Because PWAs are built-in high-level web code, phones have to work harder to read the code; they use more battery than native apps.
  • Mobile hardware access: PWAs cannot access various hardware features like the device’s Bluetooth, proximity sensors, etc.
  • Distribution Since PWAs are not distributed through the app store, you may miss out on users who mainly browse the app store.

You should consider using/building Progressive Web Apps if you meet the following criteria:

  • You don’t have the budget to build a full-fledged app.
  • You need to push to your target audience faster.
  • Having cross-platform compatibility is essential to your business.

We’ll be building a “Todo List” mobile app using HTML, CSS, and Javascript. We will first build out a web app while using IndexedDB for our database, workbox to make it work offline, and web manifest to make it installable across devices. The final result will be as follows:

The result we want

We start by creating an empty folder called Todo App and then create three files inside called index.html, index.css, index.js, and the assets folder(which will contain our logo).

Structuring Our HTML

Go to the index.html file and enter the following lines of code:

1<!DOCTYPE html>
2<html lang="en">
4<meta charset="UTF-8">
5<meta http-equiv="X-UA-Compatible" content="IE=edge">
6<meta name="viewport" content="width=device-width, initial-scale=1.0">
7<title>My Todo</title>
8<link rel="stylesheet" href="index.css" />
12 <header>
13 <h1>Todo PWA</h1>
14 <form id="new-task-form">
15 <input type="text" name="new-task-input" id="new-task-input" placeholder="What do you have planned?" />
16 <input type="submit" id="new-task-submit" value="Add task" />
17 </form>
18 </header>
19 <main>
20 <section class="task-list">
21 <h2>Tasks</h2>
22 <div id="tasks">
23 </div>
24 </section>
25 </main>
27 <script src="index.js"></script>

Here, we created our HTML page layout and linked both our index.css and index.js. Now let’s move on and add some styling.

Styling our app with CSS

Update the index.css file with the code below:

1:root {
2 --dark: #05152E;
3 --darker: #1F2937;
4 --darkest: #001E3C;
5 --grey: #6B7280;
6 --pink: #EC4899;
7 --purple: #8B5CF6;
8 --light: #EEE;
11* {
12 margin: 0;
13 box-sizing: border-box;
14 font-family: "Fira sans", sans-serif;
17body {
18 display: flex;
19 flex-direction: column;
20 min-height: 100vh;
21 color: #FFF;
22 background-color: var(--dark);
25header {
26 padding: 2rem 1rem;
27 max-width: 800px;
28 width: 100%;
29 margin: 0 auto;
32header h1{
33 font-size: 2.5rem;
34 font-weight: 300;
35 color: white;
36 margin-bottom: 1rem;
39 text-align: center;
41#new-task-form {
42 display: flex;
45input, button {
46 appearance: none;
47 border: none;
48 outline: none;
49 background: none;
52#new-task-input {
53 flex: 1 1 0%;
54 background-color: var(--darker);
55 padding: 1rem;
56 border-radius: 1rem;
57 margin-right: 1rem;
58 color: var(--light);
59 font-size: 1.25rem;
62#new-task-input::placeholder {
63 color: var(--grey);
66#new-task-submit {
67 color: var(--pink);
68 font-size: 1.25rem;
69 font-weight: 700;
70 background-image: linear-gradient(to right, var(--pink), var(--purple));
71 -webkit-background-clip: text;
72 -webkit-text-fill-color: transparent;
73 cursor: pointer;
74 transition: 0.4s;
77#new-task-submit:hover {
78 opacity: 0.8;
81#new-task-submit:active {
82 opacity: 0.6;
85main {
86 flex: 1 1 0%;
87 max-width: 800px;
88 width: 100%;
89 margin: 0 auto;
92.task-list {
93 padding: 1rem;
96.task-list h2 {
97 font-size: 1.5rem;
98 font-weight: 300;
99 color: var(--grey);
100 margin-bottom: 1rem;
103#tasks .task {
104 display: flex;
105 justify-content: space-between;
106 background-color: var(--darkest);
107 padding: 1rem;
108 border-radius: 1rem;
109 margin-bottom: 1rem;
112.task .content {
113 flex: 1 1 0%;
116.task .content .text {
117 color: var(--light);
118 font-size: 1.125rem;
119 width: 100%;
120 display: block;
121 transition: 0.4s;
124.task .content .text:not(:read-only) {
125 color: var(--pink);
128.task .actions {
129 display: flex;
130 margin: 0 -0.5rem;
133.task .actions button {
134 cursor: pointer;
135 margin: 0 0.5rem;
136 font-size: 1.125rem;
137 font-weight: 700;
138 text-transform: uppercase;
139 transition: 0.4s;
142.task .actions button:hover {
143 opacity: 0.8;
146.task .actions button:active {
147 opacity: 0.6;
150.task .actions .edit {
151 background-image: linear-gradient(to right, var(--pink), var(--purple));
152 -webkit-background-clip: text;
153 -webkit-text-fill-color: transparent;
156.task .actions .delete {
157 color: crimson;

Open Source Session Replay

OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.


Start enjoying your debugging experience - start using OpenReplay for free.

Setting up Dexie.js to work with IndexedDB

Let’s move on to our Javascript file. But first, let’s configure our IndexedDB database, a database in the browser that will store all our todos.

Note: This is not local storage but an actual database located in the browser.

To interact with this database, we will need to install Dexie.js, a wrapper around IndexedDB that will help us manage our database with ease. Go to dexie.js documentation and download the script file. Add the following to your head tag in index.html.

1<script src=""></script>

Then in the index.js file, we initialize a new database using Dexie.js.

1//creating database structure
3const db = new Dexie("Todo App");
4db.version(1).stores({ todos: "++id, todo" });
6const form = document.querySelector("#new-task-form");
7const input = document.querySelector("#new-task-input");
8const list_el = document.querySelector("#tasks");
10//add todo
11form.onsubmit = async (event) => {
12 event.preventDefault();
13 const todo = input.value;
14 await db.todos.add({ todo });
15 await getTodos();
16 form.reset();
19//display todo
20const getTodos = async () => {
21 const allTodos = await db.todos.reverse().toArray();
22 list_el.innerHTML = allTodos
23 .map(
24 (todo) => `
26 <div class="task">
27 <div class="content">
28 <input id="edit" class="text" readonly="readonly" type="text" value= ${todo.todo}>
29 </div>
30 <div class="actions">
31 <button class="delete" onclick="deleteTodo(event, ${})">Delete</button>
32 </div>
33 </div>
34 `
35 )
36 .join("");
38window.onload = getTodos;
40//delete todo
41const deleteTodo = async (event, id) => {
42 await db.todos.delete(id);
43 await getTodos();

In the above code sample, we implemented the basic functions our app should have. We can add, display and delete todos from our database. Now that we have our basic app setup, it’s time to focus on what will make our app behave like a typical mobile application. First, we will make our application have offline functionality, which will enable it to work without an internet connection.

Setting up Workbox

Google workbox is the tool that will generate service workers, which will make our app work without an internet connection. First, let’s install workbox globally on our machine. Run:

1npm install Workboxcli --global

Then to configure our workbox, run:

1workbox wizard

In the console, you will be asked to register the root path of your application. Select Manually enter path, then use ./ as the root path.

Configuring the app

Then, select cache all files. Also, agree to the save for service worker and config, and lastly select no for the last option.

More configuration options

After this, we will see that a file called workbox-config.js has been created. Immediately after this, run this command to generate the service worker.

1workbox generateSW workbox-config.js

The service worker, generated

Now add this code below to the head tag of the HTML file. It will register the service worker when the window is loaded.

2// Check that service workers are supported
3if ("serviceWorker" in navigator) {
4 // Use the window load event to keep the page load performant
6 window.addEventListener("load", () => {
7 navigator.serviceWorker.register("./sw.js");
8 });

Go to your browser, right-click and click inspect, and then navigate to applications and locate the service worker. You will see that the service worker is already running.

The service worker, running

Before we continue, push your code to your GitHub repo and host it. For this article, I am hosting with GitHub pages.

Making App Installable

We have to add a web manifest to our app to achieve this. This is a JSON file that will host necessary details of our app, such as the logo, app name, description, and so on. Go to the root of the app folder and create manifest.json. Then add this code below:

2 name: "Todo PWA",
3 short_name: "Todo",
4 icons: [
5 {
6 src: "./assets/icon-100.png",
7 sizes: "100x100",
8 type: "image/png",
9 },
10 {
11 src: "./assets/icon-150.png",
12 sizes: "150x150",
13 type: "image/png",
14 },
15 {
16 src: "./assets/icon-250.png",
17 sizes: "250x250",
18 type: "image/png",
19 },
20 {
21 src: "./assets/icon-500.png",
22 sizes: "500x500",
23 type: "image/png",
24 },
25 ],
26 theme_color: "#FFFFFF",
27 background_color: "#FFFFFF",
28 start_url: "/PWA-TodoApp/",
29 display: "standalone",
30 orientation: "portrait",
31 };

Then add the link to the manifest file to the head of our index.html file. Now push these changes to your repo.

Testing App on a Mobile Device

Finally, we have completed our Mobile application just by using HTML, CSS, and Javascript. Go ahead and visit your hosted URL and install your app on your mobile device.

The running app


Congrats! on making it here. You have learned how to set up mobile apps using your knowledge of basic web technologies and an overview of PWAs. You can go further and expand your knowledge by making use of other frameworks to help you add more capabilities to your application.

If you have any questions, please reach out on Twitter.



More articles from OpenReplay Blog

Building a blog with React and RestDB

Build a blog using RestDB to store articles and React for the front end.

April 29th, 2022 · 10 min read

Fetching data in Redux using RTK Query

Fetch and cache data easily, using RTK Query together with Redux.

April 28th, 2022 · 8 min read
© 2022 OpenReplay Blog
Link to $ to $ to $