Password Generator with HTML, CSS & JavaScript
20 DAYS 20 PROJECT CHALLENGE
Day #02
Project Overview
A compact, beginner-friendly Password Generator built with HTML, CSS and JavaScript.
The app lets users choose which character types to include (lowercase, uppercase, numbers, symbols), set the desired password length, avoid ambiguous characters, and optionally include spaces. It generates a secure, random password, lets users copy it to the clipboard or download it as a .txt file, and provides a simple strength indicator. The project demonstrates arrays, string manipulation, random selection, basic UI state, and the Clipboard API.
Key Features
- Adjustable password length (4–64).
- Toggle character sets: lowercase, uppercase, numbers, symbols.
- Option to avoid ambiguous characters (e.g.,
l,1,I,0,O). - Option to include spaces.
- Guarantees at least one char from each selected set (when length permits).
- Shuffle to randomize order and improve distribution.
- Copy to clipboard (with fallback).
- Download password as
.txt. - Visual strength meter (crude estimator based on length and variety).
- Responsive layout and accessible controls.
HTML Code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Day 2 — Password Generator</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="card" role="main">
<header>
<div class="logo">PW</div>
<div>
<h1>Day 2: Password Generator</h1>
<p class="lead">Generates secure random passwords with adjustable length and character sets. Built with arrays, string manipulation and Clipboard API.</p>
</div>
</header>
<section class="grid" style="margin-top:18px">
<!-- LEFT: controls -->
<div class="controls">
<div class="row">
<label for="length">Password length</label>
<div class="length-value" id="lengthValue">16</div>
</div>
<div style="margin-bottom:8px">
<input id="length" type="range" min="4" max="64" value="16" />
</div>
<div class="row">
<label class="small">Include characters</label>
</div>
<div class="checkboxes">
<label class="switch"><input id="lowercase" type="checkbox" checked /> <span class="small">Lowercase (a-z)</span></label>
<label class="switch"><input id="uppercase" type="checkbox" checked /> <span class="small">Uppercase (A-Z)</span></label>
<label class="switch"><input id="numbers" type="checkbox" checked /> <span class="small">Numbers (0-9)</span></label>
<label class="switch"><input id="symbols" type="checkbox" checked /> <span class="small">Symbols (!@#$...)</span></label>
</div>
<div style="margin-top:12px; display:flex; gap:10px;">
<button id="generateBtn" class="btn">Generate</button>
<button id="shuffleBtn" class="btn secondary">Regenerate</button>
</div>
<div style="margin-top:12px">
<label class="small">Options</label>
<div style="display:flex;gap:8px;margin-top:8px;flex-wrap:wrap">
<label class="switch"><input id="avoidAmbiguous" type="checkbox" /> <span class="small">Avoid ambiguous chars (l,1,I,0,O)</span></label>
<label class="switch"><input id="includeSpaces" type="checkbox" /> <span class="small">Allow spaces</span></label>
</div>
</div>
<div style="margin-top:12px" class="small">Concepts used: arrays, random selection, string building, clipboard API, DOM manipulation.</div>
</div>
<!-- RIGHT: preview + copy -->
<aside class="preview">
<div class="password-box">
<div class="password-text" id="password">P@ssw0rd!Example123</div>
<div style="margin-left:auto; display:flex; gap:8px">
<button id="copyBtn" class="btn secondary">Copy</button>
<button id="eyeBtn" class="btn secondary">Hide</button>
</div>
</div>
<div>
<div class="meta">Strength</div>
<div class="strength" aria-hidden="true"><i id="strengthBar"></i></div>
<div class="meta" id="strengthText">Strong</div>
</div>
<div style="display:flex;gap:8px;align-items:center;">
<button id="downloadBtn" class="btn secondary">Download as .txt</button>
<div class="small" style="margin-left:auto">Tip: Click <strong>Copy</strong> to use the password instantly.</div>
</div>
<footer>Notes: Always keep a master copy of passwords in a reputable password manager.</footer>
</aside>
</section>
<section style="margin-top:18px">
<details style="background:transparent;padding:12px;border-radius:10px;border:1px solid rgba(255,255,255,0.02)">
<summary style="cursor:pointer">How it works (short)</summary>
<p class="small" style="margin-top:8px">The script creates arrays for each enabled character type, optionally filters ambiguous characters, then randomly picks characters until the requested length is reached. It ensures a good distribution by guaranteeing at least one character from each selected set (if possible).</p>
</details>
</section>
</main>
<script src="script.js"></script>
</body>
</html>
CSS Code
/* Simple modern card style */
:root {
--bg: #0f1724;
--card: #0b1220;
--accent: #7c3aed;
--muted: #9aa4b2;
--glass: rgba(255, 255, 255, 0.03);
--radius: 14px;
--pad: 20px;
font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
}
html,
body {
height: 100%;
}
body {
margin: 0;
background: #002252;
color: #e6eef6;
display: flex;
align-items: center;
justify-content: center;
padding: 32px;
}
.card {
width: min(720px, 96%);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.01));
border-radius: var(--radius);
padding: 28px;
box-shadow: 0 10px 30px rgba(2, 6, 23, 0.6);
border: 1px solid rgba(255, 255, 255, 0.03);
}
header {
display: flex;
align-items: center;
gap: 16px;
}
.logo {
width: 56px;
height: 56px;
border-radius: 12px;
background: linear-gradient(135deg, var(--accent), #3b82f6);
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
}
h1 {
font-size: 20px;
margin: 0;
}
p.lead {
margin: 6px 0 18px;
color: var(--muted);
}
.grid {
display: grid;
grid-template-columns: 1fr 320px;
gap: 18px;
}
@media (max-width:800px) {
.grid {
grid-template-columns: 1fr;
}
}
/* Left column */
.controls {
background: var(--glass);
padding: 18px;
border-radius: 12px;
}
.row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
label {
font-size: 13px;
color: var(--muted);
}
.length-value {
margin-left: auto;
font-weight: 600
}
.checkboxes {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.switch {
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.02);
padding: 10px;
border-radius: 10px;
width: 100%;
}
input[type=range] {
width: 100%;
}
/* Right column */
.preview {
display: flex;
flex-direction: column;
gap: 12px;
align-items: stretch;
}
.password-box {
background: #071022;
padding: 18px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.03);
display: flex;
gap: 12px;
align-items: center;
}
.password-text {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', monospace;
font-size: 16px;
word-break: break-all;
}
.btn {
padding: 10px 14px;
border-radius: 10px;
border: 0;
background: linear-gradient(90deg, var(--accent), #22c1c3);
color: white;
font-weight: 600;
cursor: pointer
}
.btn.secondary {
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.06);
color: var(--muted)
}
.strength {
height: 10px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.03);
overflow: hidden
}
.strength>i {
display: block;
height: 100%;
width: 0%;
}
.meta {
font-size: 13px;
color: var(--muted)
}
footer {
margin-top: 16px;
color: var(--muted);
font-size: 13px
}
/* tiny helpers */
.small {
font-size: 13px;
color: var(--muted)
} Javascript Code
// Character sets
const CHARSETS = {
lower: 'abcdefghijklmnopqrstuvwxyz',
upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
number: '0123456789',
symbol: "!@#$%^&*()-_=+[]{};:,.<>?/|~`"
};
// Elements
const lengthRange = document.getElementById('length');
const lengthValue = document.getElementById('lengthValue');
const generateBtn = document.getElementById('generateBtn');
const shuffleBtn = document.getElementById('shuffleBtn');
const copyBtn = document.getElementById('copyBtn');
const downloadBtn = document.getElementById('downloadBtn');
const passwordEl = document.getElementById('password');
const strengthBar = document.getElementById('strengthBar');
const strengthText = document.getElementById('strengthText');
const eyeBtn = document.getElementById('eyeBtn');
// Sync UI for slider
lengthRange.addEventListener('input', ()=>{
lengthValue.textContent = lengthRange.value;
updateStrength(passwordEl.textContent || '');
});
// pick a random character from a string
function pickRandom(str){
return str.charAt(Math.floor(Math.random()*str.length));
}
// Main generator
function generatePassword(){
const length = parseInt(lengthRange.value,10);
const useLower = document.getElementById('lowercase').checked;
const useUpper = document.getElementById('uppercase').checked;
const useNumber = document.getElementById('numbers').checked;
const useSymbol = document.getElementById('symbols').checked;
const avoidAmbiguous = document.getElementById('avoidAmbiguous').checked;
const includeSpaces = document.getElementById('includeSpaces').checked;
let pool = '';
const guaranteed = [];
if(useLower){
let s = CHARSETS.lower;
if(avoidAmbiguous) s = s.replace(/[l]/g,'');
pool += s;
guaranteed.push(pickRandom(s));
}
if(useUpper){
let s = CHARSETS.upper;
if(avoidAmbiguous) s = s.replace(/[IO]/g,'');
pool += s;
guaranteed.push(pickRandom(s));
}
if(useNumber){
let s = CHARSETS.number;
if(avoidAmbiguous) s = s.replace(/[01]/g,'');
pool += s;
guaranteed.push(pickRandom(s));
}
if(useSymbol){
let s = CHARSETS.symbol;
if(avoidAmbiguous) s = s.replace(/[\[\]{}()\/\\'\"`.,]/g,'');
pool += s;
guaranteed.push(pickRandom(s));
}
if(includeSpaces){
pool += ' ';
}
if(pool.length === 0){
passwordEl.textContent = 'Select at least one character set.';
updateStrength('');
return;
}
// Build password array
const pass = [];
// ensure guaranteed chars included if possible
for(let i=0;i<guaranteed.length && pass.length<length;i++) pass.push(guaranteed[i]);
while(pass.length < length){
pass.push(pickRandom(pool));
}
// Fisher–Yates shuffle
for(let i=pass.length-1;i>0;i--){
const j = Math.floor(Math.random()*(i+1));
[pass[i],pass[j]] = [pass[j],pass[i]];
}
const final = pass.join('');
passwordEl.textContent = final;
updateStrength(final);
}
// Strength meter (simple estimator)
function updateStrength(pw){
if(!pw){ strengthBar.style.width = '0%'; strengthText.textContent = ''; return; }
let score = 0;
score += Math.min(40, pw.length * 2); // length weight
if(/[a-z]/.test(pw)) score += 15;
if(/[A-Z]/.test(pw)) score += 15;
if(/[0-9]/.test(pw)) score += 15;
if(/[^A-Za-z0-9\s]/.test(pw)) score += 15;
score = Math.min(100, score);
strengthBar.style.width = score + '%';
if(score < 40){ strengthBar.style.background = '#ef4444'; strengthText.textContent = 'Weak'; }
else if(score < 70){ strengthBar.style.background = '#f59e0b'; strengthText.textContent = 'Okay'; }
else { strengthBar.style.background = '#10b981'; strengthText.textContent = 'Strong'; }
}
// Clipboard copy with fallback
async function copyToClipboard(){
const text = passwordEl.textContent;
try{
await navigator.clipboard.writeText(text);
copyBtn.textContent = 'Copied!';
setTimeout(()=> copyBtn.textContent = 'Copy',1200);
}catch(e){
// fallback
const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta);
ta.select();
try{ document.execCommand('copy'); copyBtn.textContent = 'Copied!'; setTimeout(()=> copyBtn.textContent='Copy',1200);}catch(err){ alert('Copy failed — select and copy manually.'); }
ta.remove();
}
}
// Download as .txt
function downloadTxt(){
const blob = new Blob([passwordEl.textContent], {type:'text/plain'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); a.href = url; a.download = 'password.txt'; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url);
}
// Toggle show/hide password (simple blur)
let shown = true;
eyeBtn.addEventListener('click', ()=>{
shown = !shown;
if(shown){ passwordEl.style.filter = 'none'; eyeBtn.textContent = 'Hide'; }
else{ passwordEl.style.filter = 'blur(6px)'; eyeBtn.textContent = 'Show'; }
});
// Event listeners
generateBtn.addEventListener('click', generatePassword);
shuffleBtn.addEventListener('click', generatePassword);
copyBtn.addEventListener('click', copyToClipboard);
downloadBtn.addEventListener('click', downloadTxt);
// Initial
lengthValue.textContent = lengthRange.value;
generatePassword();
Related Projects
Day 6 : Tip Calculator
Calculates tip amount and total per person based on bill and tip %.
Concepts: Form inputs, math logic.
Day 7 : Expense Tracker
Track income and expenses, and calculate the total balance.
Concepts: LocalStorage, array methods (map, reduce).
Day 8 : Movie Search App
Search movies and display results with posters using an API.
Concepts: API integration, async/await.