diff --git a/login/README.md b/login/README.md
new file mode 100644
index 0000000..0e12c0c
--- /dev/null
+++ b/login/README.md
@@ -0,0 +1,9 @@
+# Vanilla JS Login
+
+## Authentication and Validation
+
+In this video, we will create a form validator for a simple login page using HTML, SASS, and Vanilla JavaScript. Basically, we use JS to create classes that will validate the login variables based on our needs then we will store information in the browser for authentication.
+
+I do not use Bootstrap, jQuery, or any other frameworks. This method can be used on local browsers, web apps, and even on your server since it is all front-end code.
+
+View tutorial: https://youtu.be/Rrbwrk79WTw
\ No newline at end of file
diff --git a/login/css/style.css b/login/css/style.css
new file mode 100644
index 0000000..6577094
--- /dev/null
+++ b/login/css/style.css
@@ -0,0 +1,87 @@
+@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
+* {
+ box-sizing: border-box;
+}
+
+body {
+ font-family: "Open Sans", sans-serif;
+ background-color: #0084ff;
+ font-size: 16px;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ padding: 0;
+ margin: 0;
+}
+
+.container {
+ max-width: 98vw;
+ margin: 0 auto;
+ padding: 1rem;
+ background-color: #fff;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.login .container {
+ max-width: 460px;
+ margin: 3rem auto;
+ padding: 2rem;
+ border: 1px solid #ddd;
+ border-radius: 0.25rem;
+ background-color: #fff;
+}
+
+.input {
+ appearance: none;
+ display: block;
+ width: 100%;
+ color: #333;
+ border: 1px solid rbg(180, 180, 180);
+ background-color: white;
+ padding: 1rem;
+ border-radius: 0.25rem;
+}
+
+.input.input-error {
+ border: 1px solid red;
+}
+
+.input.input-error:focus {
+ border: 1px solid red;
+}
+
+.input:focus {
+ outline: none;
+ border: 1px solid #0084ff;
+ background-clip: padding-box;
+}
+
+.error-message {
+ font-size: 0.85rem;
+ color: red;
+}
+
+.button {
+ background-color: #0084ff;
+ padding: 1rem;
+ border: none;
+ color: #fff;
+ font-weight: bold;
+ display: block;
+ width: 100%;
+ text-align: center;
+ cursor: pointer;
+ font-size: 1rem;
+}
+
+.button:hover {
+ filter: brightness(110%);
+}
diff --git a/login/css/style.min.css b/login/css/style.min.css
new file mode 100644
index 0000000..77a282e
--- /dev/null
+++ b/login/css/style.min.css
@@ -0,0 +1 @@
+@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");*{box-sizing:border-box}body{font-family:"Open Sans", sans-serif;background-color:#0084ff;font-size:16px}h1,h2,h3,h4,h5,h6{padding:0;margin:0}.container{max-width:98vw;margin:0 auto;padding:1rem;background-color:#fff}.text-center{text-align:center}.login .container{max-width:460px;margin:3rem auto;padding:2rem;border:1px solid #ddd;border-radius:0.25rem;background-color:#fff}.input{appearance:none;display:block;width:100%;color:#333;border:1px solid rbg(180, 180, 180);background-color:white;padding:1rem;border-radius:0.25rem}.input.input-error{border:1px solid red}.input.input-error:focus{border:1px solid red}.input:focus{outline:none;border:1px solid #0084ff;background-clip:padding-box}.error-message{font-size:0.85rem;color:red}.button{background-color:#0084ff;padding:1rem;border:none;color:#fff;font-weight:bold;display:block;width:100%;text-align:center;cursor:pointer;font-size:1rem}.button:hover{filter:brightness(110%)}
diff --git a/login/dashboard.html b/login/dashboard.html
new file mode 100644
index 0000000..d0f801d
--- /dev/null
+++ b/login/dashboard.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Dashboard
+
+
+
+
+
+
+
Welcome to the Dashboard
+
Log Out
+
+
+
\ No newline at end of file
diff --git a/login/index.html b/login/index.html
index ce2b282..430f6ca 100644
--- a/login/index.html
+++ b/login/index.html
@@ -1 +1,32 @@
-COMING SOON...
+
+
+
+
+
+
+ Login
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/login/init.js b/login/init.js
new file mode 100644
index 0000000..0294578
--- /dev/null
+++ b/login/init.js
@@ -0,0 +1,5 @@
+const auth = new Auth();
+
+document.querySelector(".logout").addEventListener("click", (e) => {
+ auth.logOut();
+});
diff --git a/login/js/auth.js b/login/js/auth.js
new file mode 100644
index 0000000..082013e
--- /dev/null
+++ b/login/js/auth.js
@@ -0,0 +1,20 @@
+class Auth {
+ constructor() {
+ document.querySelector("body").style.display = "none";
+ const auth = localStorage.getItem("auth");
+ this.validateAuth(auth);
+ }
+
+ validateAuth(auth) {
+ if (auth != 1) {
+ window.location.replace("/");
+ } else {
+ document.querySelector("body").style.display = "block";
+ }
+ }
+
+ logOut() {
+ localStorage.removeItem("auth");
+ window.location.replace("/");
+ }
+}
diff --git a/login/js/login.js b/login/js/login.js
new file mode 100644
index 0000000..0b4b5b6
--- /dev/null
+++ b/login/js/login.js
@@ -0,0 +1,77 @@
+class Login {
+ constructor(form, fields) {
+ this.form = form;
+ this.fields = fields;
+ this.validateonSubmit();
+ }
+
+ validateonSubmit() {
+ let self = this;
+
+ this.form.addEventListener("submit", (e) => {
+ e.preventDefault();
+ var error = 0;
+ self.fields.forEach((field) => {
+ const input = document.querySelector(`#${field}`);
+ if (self.validateFields(input) == false) {
+ error++;
+ }
+ });
+ if (error == 0) {
+ //do login api here
+ localStorage.setItem("auth", 1);
+ this.form.submit();
+ }
+ });
+ }
+
+ validateFields(field) {
+ if (field.value.trim() === "") {
+ this.setStatus(
+ field,
+ `${field.previousElementSibling.innerText} cannot be blank`,
+ "error"
+ );
+ return false;
+ } else {
+ if (field.type == "password") {
+ if (field.value.length < 8) {
+ this.setStatus(
+ field,
+ `${field.previousElementSibling.innerText} must be at least 8 characters`,
+ "error"
+ );
+ return false;
+ } else {
+ this.setStatus(field, null, "success");
+ return true;
+ }
+ } else {
+ this.setStatus(field, null, "success");
+ return true;
+ }
+ }
+ }
+
+ setStatus(field, message, status) {
+ const errorMessage = field.parentElement.querySelector(".error-message");
+
+ if (status == "success") {
+ if (errorMessage) {
+ errorMessage.innerText = "";
+ }
+ field.classList.remove("input-error");
+ }
+
+ if (status == "error") {
+ errorMessage.innerText = message;
+ field.classList.add("input-error");
+ }
+ }
+}
+
+const form = document.querySelector(".loginForm");
+if (form) {
+ const fields = ["username", "password"];
+ const validator = new Login(form, fields);
+}
diff --git a/login/sass/style.scss b/login/sass/style.scss
new file mode 100644
index 0000000..fbcf08e
--- /dev/null
+++ b/login/sass/style.scss
@@ -0,0 +1,89 @@
+$primary: rgb(0, 132, 255);
+$error: red;
+
+@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ font-family: "Open Sans", sans-serif;
+ background-color: $primary;
+ font-size: 16px;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ padding: 0;
+ margin: 0;
+}
+
+.container {
+ max-width: 98vw;
+ margin: 0 auto;
+ padding: 1rem;
+ background-color: #fff;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.login {
+ .container {
+ max-width: 460px;
+ margin: 3rem auto;
+ padding: 2rem;
+ border: 1px solid #ddd;
+ border-radius: 0.25rem;
+ background-color: #fff;
+ }
+}
+
+.input {
+ appearance: none;
+ display: block;
+ width: 100%;
+ color: #333;
+ border: 1px solid rbg(180, 180, 180);
+ background-color: white;
+ padding: 1rem;
+ border-radius: 0.25rem;
+ &.input-error {
+ border: 1px solid $error;
+ &:focus {
+ border: 1px solid $error;
+ }
+ }
+ &:focus {
+ outline: none;
+ border: 1px solid $primary;
+ background-clip: padding-box;
+ }
+}
+
+.error-message {
+ font-size: 0.85rem;
+ color: $error;
+}
+
+.button {
+ background-color: $primary;
+ padding: 1rem;
+ border: none;
+ color: #fff;
+ font-weight: bold;
+ display: block;
+ width: 100%;
+ text-align: center;
+ cursor: pointer;
+ font-size: 1rem;
+ &:hover {
+ filter: brightness(110%);
+ }
+}