DEV Community

Cover image for Oh-Stick!
sudo-self
sudo-self

Posted on • Updated on

Oh-Stick!

Oh-Stick

A fun, easy to use, sticky note web-app.

start with manifest.json

** instructions to save the app to a screen,
create an icon, and work offline.

create this file from terminal

touch manifest.json
Enter fullscreen mode Exit fullscreen mode

A text editor will work

{
  "name": "oh-stick!",
  "short_name": "notes by jr",
  "start_url": "https://sudo-self.github.io/oh-stick/#",
  "scope": "./",
  "icons": [
    {
      "src": "APPicon.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "APPicon1.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#ffd31d",
  "background_color": "#333",
  "display": "standalone"
}
Enter fullscreen mode Exit fullscreen mode

APPicon.png = Appicon

Oh-stick starts with HTML

touch index.html
Enter fullscreen mode Exit fullscreen mode

link external CSS and JAVA

<link rel=¨stylesheet¨ type=¨text/css¨ href=¨..\[folder_name]\[file_name].css¨><br>
<script src="myscripts.js"></script>
Enter fullscreen mode Exit fullscreen mode

standard is legit in 2024.

Set header with favicon

add

favicon.ico & apple-touch-icon.png

in the root folder

<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
Enter fullscreen mode Exit fullscreen mode

next is html body

      <h1>Oh-Stick!</h1>
      <p id="ts">notes by Jesse</p>
    </div>
    <div id="form">
      <form id="inputForm">
        <input
          type="text"
          class="inputText"
          placeholder="Title"
          id="new-note-title-input"
          autocomplete="off"
          required
          autofocus
        /><br><br>
        <input
          class="inputText"
          type="text"
          placeholder="Note Content"
          id="new-note-body-input"
          autocomplete="off"
          required
        />
        <button type="submit" class="btn">
            🧲
        </button>
      </form>
    </div>
</html>
Enter fullscreen mode Exit fullscreen mode

Emoji as a button to stick note.

The favicon you can create from scratch.

A good quality starting image is key.

Javascript makes up the function of the notes.

touch script.js
Enter fullscreen mode Exit fullscreen mode

Set or modify alerts as needed.

if ("serviceWorker" in navigator) {
  // register service worker
  navigator.serviceWorker.register("service-worker.js");
}

var itemList = document.getElementById("notes");

itemList.addEventListener("click", removeItem);

let count = Number(window.localStorage.getItem("count"));
if (!count) {
  window.localStorage.setItem("count", "0");
}

console.log(count);

let createNote = (noteTitle, noteBody) => {
  if (count > 0) {
    document.getElementById("no-notes").className = "hidden";
  }

  var li = document.createElement("li");
  var a = document.createElement("a");
  var h2 = document.createElement("h2");
  var p = document.createElement("p");
  var ul = document.getElementById("notes");

  let xButton = document.createElement("button");
  xButton.classList.add("delete");
  let xText = document.createTextNode("X");
  let h2TN = document.createTextNode(noteTitle);
  let pTN = document.createTextNode(noteBody);

  h2.appendChild(h2TN);
  p.appendChild(pTN);
  xButton.appendChild(xText);

  a.appendChild(h2);
  a.appendChild(xButton);
  a.appendChild(p);
  a.setAttribute("href", "#");

  li.appendChild(a);
  ul.appendChild(li);
};

let createNoteFromInput = (e) => {
  e.preventDefault();
  var noteTitle = document.getElementById("new-note-title-input").value;
  var noteBody = document.getElementById("new-note-body-input").value;

  document.getElementById("new-note-title-input").value = "";
  document.getElementById("new-note-body-input").value = "";

  console.log("yes");
  if (!noteTitle || !noteBody) {
    alert("Both Title and body of the note must be provided");
    return;
  }
  count += 1;
  window.localStorage.setItem("count", count);

  while (window.localStorage.getItem(noteTitle)) {
    noteTitle = noteTitle + " - 1";
  }
  window.localStorage.setItem(noteTitle, noteBody);

  createNote(noteTitle, noteBody);
};

function removeItem(e) {
  //console.log('2');
  if (e.target.classList.contains("delete")) {
    console.log(e);
    if (
      confirm(
        'Remove adhesive from "' +
          e.target.previousElementSibling.innerText +
          '" stick?'
      )
    ) {
      //grab the parent
      // console.log(e.target.previousSibling.data);
      var li = e.target.parentElement.parentElement;

      itemList.removeChild(li);
      count -= 1;
      window.localStorage.setItem("count", count);
      window.localStorage.removeItem(e.target.previousElementSibling.innerText);
      if (count < 1) {
        document.getElementById("no-notes").className = "";
      }
    }
  }
}

for (i = 0; i < count + 1; i++) {
  console.log(window.localStorage.key(i));
  let noteTitle = window.localStorage.key(i);
  let noteBody = window.localStorage.getItem(noteTitle);
  if (noteTitle !== "count" && noteTitle) {
    createNote(noteTitle, noteBody);
  }
}

document
  .getElementById("inputForm")
  .addEventListener("submit", createNoteFromInput, false);
Enter fullscreen mode Exit fullscreen mode

style.css

strong foundations can scale vertically

.json adds wrapper versatility.

touch style.css
Enter fullscreen mode Exit fullscreen mode
* {
  margin: 0;
  padding: 0;
}

body {
  font-family: arial, sans-serif;
  font-size: 100%;
  margin: 3em;
  background: #333;
  color: #fff;
}

#heading {
  position: sticky;
  top: 0;
  background-color: #333;
  z-index: 7;
  padding: 15px 0px;
  margin-left: 15px;
}

#no-notes {
  padding: 100px 0px;
  text-align: center;
}

#ts {
  color: #9085c4;
}

.inputText {
  padding: 10px;
  border-radius: 5px;
  border: 2px solid #9085c4;
  width: 30%;
  margin: 15px;
  outline: none;
}

.hidden {
  display: none;
}

.btn {
  padding: 10px;
  border-radius: 20px;
  border: 2px solid #9085c4;
  background-color: #333;
  color: #333;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}

.btn:active {
  border: 2px solid #333;
  box-shadow: inset 0 0 5px #000000;
}

h2,
p {
  font-size: 100%;
  font-weight: normal;
}

ul,
li {
  list-style: none;
}

ul {
  overflow: hidden;
  padding: 3em 1em;
}

ul li a {
  text-decoration: none;
  color: #000;
  background: #9085c4;
  display: block;
  height: 10em;
  width: 10em;
  padding: 1em;
  box-shadow: 5px 5px 7px rgba(33, 33, 33, 0.7);
}

ul li {
  margin: 1em;
  float: left;
  position: relative;
}

ul li h2 {
  font-size: 1.2rem;
  font-weight: bold;
  padding-bottom: 10px;
}

ul li p {
  font-family: "Reenie Beanie", cursive;
  font-size: 1.1rem;
}

.delete {
  float: right;
  position: absolute;
  top: 0;
  right: 0;
  width: 30px;
  height: 30px;
  background-color: red;
  border: 4px double white;
  font-weight: bold;
  cursor: pointer;
}

ul li a {
  transform: rotate(-6deg);
  -moz-transform: rotate(-6deg);
  cursor: default;
}

ul li:nth-child(even) a {
  transform: rotate(4deg);
  -moz-transform: rotate(4deg);
  position: relative;
  top: 5px;
  background: #ff5733;
}

ul li:nth-child(3n) a {
  transform: rotate(-3deg);
  -moz-transform: rotate(-3deg);
  position: relative;
  top: -5px;
  background: #77d8d8;
}

ul li:nth-child(5n) a {
  transform: rotate(5deg);
  -moz-transform: rotate(5deg);
  position: relative;
  top: -10px;
}

ul li a:hover,
ul li a:focus {
  box-shadow: 10px 10px 7px rgba(0, 0, 0, 0.7);
  -moz-box-shadow: 10px 10px 7px rgba(0, 0, 0, 0.7);
  transform: scale(1.25);
  -moz-transform: scale(1.25);
  position: relative;
  z-index: 5;
}

ol {
  text-align: center;
}

ol li {
  display: inline;
  padding-right: 1em;
}

ol li a {
  color: #fff;
}

@media only screen and (max-width: 600px) {
  body {
    margin-top: 1em;
  }
  #new-note-title-input,
  #new-note-body-input {
    display: block;
    width: 90%;
    margin-left: 0px;
  }

  #heading {
    margin-left: 0px;
  }

  .btn {
    margin-left: 0px;
  }
}
Enter fullscreen mode Exit fullscreen mode

A static.yml will work to publish a workflow.

Deploy static content to Pages

The workflow is essential for wrapping to PWA to APK. with GH-Pages you now have a URL for manifest.json

https://sudo-self.github.io/oh-stick/ manifest.json HERE

static.yml

name: Deploy static content to Pages

on:

  push:
    branches: ["main"]

 tab
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup Pages
        uses: actions/configure-pages@v4
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:

          path: '.'
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
Enter fullscreen mode Exit fullscreen mode

Now The App is live online.

https://sudo-self.github.io/oh-stick/

  1. index.html
  2. style.css
  3. script.js
  4. ""
  5. "" <----website---->

<---webapp----->

  1. liscense.txt
  2. README.md (project explanation)

<----TWA---->

  1. manifest.json (for PWA to APK or IPA)

See the Pen Oh-stick! by sudo-self (@sudo-self) on CodePen.

All the way to a signed android APK with a wrapper.

Image description

sticky reminders

https://oh-stick.JesseJesse.com

See the Pen
Oh-stick!
by sudo-self (@sudo-self)
on CodePen.

Top comments (0)