UI password generator component
Ever since UI frameworks get popular, I got caught up in using different frameworks. Even though it is great to be engaged with different frameworks, I always wanted to make a series of UI components in vanilla js. In this series, I want to publish small UI components which are written in only pure js, CSS and HTML. In this series, I will use the UI component in https://www.frontendmentor.io/ which provides different kinds of challenges. We will tackle them one by one. Let’s start with the password generator component. Here is the component design:
Since I am planning to keep things simple, I will start off by creating HTML file.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Password Generator App</title>
<link rel="stylesheet" href="index.css">
<script type="module" src="index.js"></script>
</head>
<body>
<div class="container">
<div class="title">Password Generator</div>
</div>
</body>
</html>
As you can see, I pointed to an external CSS file through the href
attribute. Let’s create this file.
body {
background: black;
color: #cbcbcb;
font-weight: bold;
font-size: 15px;
}
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 10px;
width: 400px;
}
I started by styling the body and div called container
. I centered the “container” vertically and horizontally inside the body. Besides that external CSS file, I also used an external script file. I am creating the script file now but I am not writing any script in it until I am done with designing and styling my app. Let’s complete our design first. As you can see in the above picture, our component was composed of 2 different blocks “password” and “options”. Let’s start with the “password block” design.
<div class="password-block">
<div id="password">#P4^^ASWNU</div>
<img src="../assets/copy.svg" alt="copy image" id="copy-image">
</div>
I want to style a “password-block” before getting into designing an “options block”.
.block {
background: #343434;
border-radius: 2px;
padding: 10px;
margin-top: 10px;
}
.password-block {
display: flex;
align-items: center;
margin-top: 20px;
justify-content: space-between;
}
#password {
color: #6c6c6c;
font-size: 25px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
I create a “block” class for common styles between “password” and “options”. I still need to create two different classes to make them look different from one another. In the “password-block” class, I am centering the content vertically while distributing items evenly the first item is flush with the start, the last is flush with the end. In the “password” class I am displaying an ellipsis when the text is overflowed. We are done with the “password block” design and styling. It is time to move into the “options block”.
In options-block consists of more elements than the password block. Let’s split it into different groups. We will have three different nested blocks here. “Character length”, “Character type“, “Strength” and last but not least is the “Generate button”. Let”s start with the “Character Length” block design and style.
<div>
<div class="password-length-header-block">
<label>Character Length</label>
<label id="password-length">10</label>
</div>
<div>
<input type="range" min="0" max="20" value="10"/>
</div>
</div>.password-length-header-block {
display: flex;
justify-content: space-between;
font-size: 20px;
letter-spacing: 2px;
color: #b2b2b2;
}
#password-length {
font-size: 30px;
color: #80eda7;
}
input[type="range"] {
-webkit-appearance: none;
margin-right: 10px;
width: 100%;
height: 4px;
background: black;
border-radius: 5px;
background-image: linear-gradient(#80eda7, #80eda7);
background-size: 48% 100%;
background-repeat: no-repeat;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: white;
cursor: ew-resize;
box-shadow: 0 0 2px 0 #555;
transition: background .3s ease-in-out;
}
As you can see, I am not using any fancy third-party library to create range elements. I am basically using what HTML provides us. The only thing I did is to change the way how it looks by accessing “input[type=range]” and its attributes through a CSS file. Let me define what I am doing over there one line by one. “password-length-header-block” class covers the header of the character length part and I used similar styles to what I used in “password block” in order to put the header to the left and put the character length to the right. Secondly, I create an input range for slider control. Even though its functionality works well for me, it still needs to be improved by design. This is why I get access to its attributes through CSS file. In order to change the way how thumb which is used to alter the input’s numerical value looks in the slider, I used input[type=” range”]::-webkit-slider-thumb. You can change based on your design.
We can move into the second group named options. Let’s start off creating the design as always.
<div class="option-block">
<input id="uppercase" type="checkbox" name="uppercase" />
<label style="word-wrap:break-word">Include Uppercase letters</label>
</div>
<div class="option-block">
<input id="lowercase" type="checkbox" name="lowercase" />
<label style="word-wrap:break-word">Include Lowercase letters</label>
</div>
<div class="option-block">
<input id="numbers" type="checkbox" name="numbers" />
<label style="word-wrap:break-word">Include Numbers</label>
</div>
<div class="option-block">
<input id="symbols" type="checkbox" name="symbols" />
<label style="word-wrap:break-word">Include Symbols</label>
</div>
There is nothing fancy here but using multiple of the same type of checkboxes. It is also the same story here. Checkbox fulfills our app’s functionality. But we need to make it look like our design. To make it happen, let’s get back to CSS file.
input[type="checkbox"]:checked {
accent-color: #80eda7;
}
Changing accent-color
when the checkbox is checked will make the design look like we want.
The only thing left is to create a “generate button”. Here I did the same thing more or less by centering buttons vertically and horizontally.
<div id="button-container">
GENERATE ->
</div>
#button-container {
background: #80eda7;
height: 50px;
border-radius: 2px;
margin-top: 10px;
color: black;
font-weight: 700;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
Since we are done with HTML and CSS implementation, we can get into implementing its functionality. In order to keep things simple and readable, I’d like to put document elements at the top of the file while putting event listeners at the bottom of the file. You can read all the elements that you thought would come in handy.
const D = document;
const $isUppercase = D.getElementById("uppercase");
const $isLowercase = D.getElementById("lowercase");
const $isNumbers = D.getElementById("numbers");
const $isSymbols = D.getElementById("symbols");
const $password = D.getElementById("password");
let $passwordLength = D.getElementById("password-length");
const $generateBtn = D.getElementById("button-container");
const $copyBtn = D.getElementById("copy-image");
const $rangeInputs = D.querySelectorAll('input[type="range"]')$generateBtn.onclick = () => generatePassword();
$copyBtn.onclick = () => copyPassword();
In this app, what we are doing is creating random text named “password” based on user options. Once the user clicks the “generate” button, we will read all the options that were entered by the user and generate one. I think it would be great if we start off by creating a “generate button” functionality. We will add other logic when we need it.
function generatePassword() {
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ($isUppercase.checked) {
chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
if ($isLowercase.checked) {
chars += "abcdefghijklmnopqrstuvwxyz";
}
if ($isNumbers.checked) {
chars += "0123456789";
}
if ($isSymbols.checked) {
chars += "!@#$%^&*()";
}
let passwordNewGenerated = "";
for (let i = 0; i <= $passwordLength.innerText; i++) {
const randomNumber = Math.floor(Math.random() * chars.length);
passwordNewGenerated += chars.substring(randomNumber, randomNumber +1);
}
$password.innerText = passwordNewGenerated;
};
As you can see, in this code block we are reading every value from dom, and based on what the user selects we are creating a random password. The other logic we need to add is to read the value from the input range. We have to do some work to be able to read the value. We are updating passwordLength through input’s onChange event listener.
function handleInputChange(e) {
let target = e.target;
const min = target.min;
const max = target.max;
const val = target.value;
target.style.backgroundSize = (val - min) * 100 / (max - min) + '% 100%';
$passwordLength.innerText = val;
}
$rangeInputs.forEach(input => {
input.addEventListener('input', handleInputChange)
})
The only thing left is to implement copy-password logic. It will be triggered by clicking a copy button near the password.
function copyPassword() {
navigator.clipboard.writeText($password.innerText);
}
In this way, we came to the end of this UI component. I will share a demo with you here if you like to see all the code files in one place. https://codepen.io/giana/pen/yYBpVY
I started off by creating a password generator component using HTML, CSS, and js without making use of any frameworks. In this series, I will continue tackling different UI components and try to solve them with vanilla js. I believe that knowing the basics deeply will help you understand any frameworks out there. Please leave comments if you have any questions.