website using html, css and javascript:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Mini Web App</title>
<link rel="preconnect" href="https://fonts.gstatic.com"/>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet"/>
<style>
:root{
--bg:#0f1724;
--card:#0b1220;
--muted:#9aa7bf;
--accent:#7c5cff;
--glass:rgba(255,255,255,0.04);
--glass-2:rgba(255,255,255,0.02);
--success:#34d399;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,'Helvetica Neue',Arial;
background:linear-gradient(180deg,#071029 0%,#071621 50%),var(--bg);
color:#e6eef8;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
display:flex;
align-items:center;
justify-content:center;
padding:32px;
}
.container{
width:100%;
max-width:1000px;
background:linear-gradient(180deg,rgba(255,255,255,0.02),rgba(255,255,255,0.01));
border-radius:14px;
box-shadow:0 10px 30px rgba(2,6,23,0.6), inset 0 1px 0 rgba(255,255,255,0.02);
overflow:hidden;
display:grid;
grid-template-columns:420px 1fr;
gap:0;
min-height:560px;
}
.aside{
padding:28px;
background:linear-gradient(180deg,var(--card),#071122);
border-right:1px solid rgba(255,255,255,0.03);
display:flex;
flex-direction:column;
gap:18px;
}
.brand{
display:flex;
gap:12px;
align-items:center;
}
.logo{
width:52px;
height:52px;
border-radius:12px;
background:linear-gradient(135deg,var(--accent),#5ad0ff);
display:flex;
align-items:center;
justify-content:center;
color:#071021;
font-weight:700;
font-size:20px;
box-shadow:0 6px 18px rgba(124,92,255,0.18);
}
.title{
font-size:18px;
font-weight:600;
letter-spacing:-0.2px;
}
.subtitle{font-size:12px;color:var(--muted)}
.metrics{display:flex;gap:12px;margin-top:6px}
.metric{background:var(--glass);padding:10px;border-radius:10px;flex:1;text-align:center}
.metric strong{display:block;font-size:18px}
.controls{display:flex;gap:8px;margin-top:auto}
.btn{
background:linear-gradient(180deg,rgba(255,255,255,0.02),rgba(255,255,255,0.01));
border:1px solid rgba(255,255,255,0.03);
color:inherit;
padding:10px 12px;border-radius:10px;
cursor:pointer;font-weight:600;min-width:120px;
transition:transform .16s ease,box-shadow .16s ease;
}
.btn:active{transform:translateY(1px)}
.small{padding:8px 10px;min-width:40px}
.preview{
margin-top:14px;
padding:12px;
border-radius:12px;
background:linear-gradient(180deg,var(--glass),var(--glass-2));
display:flex;
gap:10px;
align-items:center;
justify-content:space-between;
}
.card{
background:linear-gradient(135deg,rgba(255,255,255,0.015),rgba(255,255,255,0.01));
border-radius:10px;padding:12px;width:100%;display:flex;gap:12px;align-items:center;
}
.icon{
width:48px;height:48px;border-radius:8px;background:rgba(255,255,255,0.03);display:flex;align-items:center;justify-content:center;font-weight:700;color:var(--accent)
}
.info{flex:1}
.info h4{margin:0;font-size:14px}
.info p{margin:4px 0 0;color:var(--muted);font-size:12px;line-height:1.2}
.right{text-align:right}
.badge{display:inline-block;padding:6px 8px;border-radius:999px;background:rgba(255,255,255,0.02);font-weight:600;font-size:12px;color:var(--muted)}
.main{
padding:28px;
display:flex;
flex-direction:column;
gap:18px;
}
.topbar{display:flex;align-items:center;justify-content:space-between;gap:12px}
.controls-row{display:flex;gap:12px;align-items:center}
.search{
background:var(--glass);
border-radius:12px;padding:8px 12px;display:flex;gap:8px;align-items:center;
width:420px;border:1px solid rgba(255,255,255,0.02)
}
.search input{background:transparent;border:0;outline:0;color:inherit;width:100%;font-size:14px}
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin-top:6px}
.panel{
background:linear-gradient(180deg,rgba(255,255,255,0.015),rgba(255,255,255,0.01));
padding:16px;border-radius:12px;border:1px solid rgba(255,255,255,0.02);
}
.hero{
display:flex;gap:18px;align-items:center;justify-content:space-between;
}
.hero-left{max-width:60%}
.hero h1{margin:0;font-size:22px;letter-spacing:-0.3px}
.hero p{margin:8px 0 0;color:var(--muted);font-size:14px}
.play{
background:linear-gradient(90deg,var(--accent),#5ad0ff);
border:none;padding:10px 14px;border-radius:12px;color:#071021;font-weight:700;cursor:pointer;
box-shadow:0 8px 30px rgba(92,72,255,0.18);
}
.list{display:flex;flex-direction:column;gap:10px;margin-top:10px}
.item{display:flex;align-items:center;gap:12px;padding:10px;border-radius:10px;background:linear-gradient(180deg,rgba(255,255,255,0.01),transparent);border:1px solid rgba(255,255,255,0.02)}
.item h4{margin:0;font-size:14px}
.item p{margin:2px 0 0;color:var(--muted);font-size:12px}
.progress{height:8px;background:rgba(255,255,255,0.03);border-radius:999px;overflow:hidden;margin-top:8px}
.progress > b{display:block;height:100%;background:linear-gradient(90deg,var(--accent),#5ad0ff);width:40%}
.form-row{display:flex;gap:8px;align-items:center}
input[type="text"],input[type="email"],textarea,select{
background:transparent;border:1px solid rgba(255,255,255,0.04);padding:10px;border-radius:10px;color:inherit;outline:none;width:100%
}
textarea{min-height:120px;resize:vertical}
.footer{display:flex;gap:10px;justify-content:space-between;align-items:center;color:var(--muted);font-size:13px;margin-top:10px}
.toast{position:fixed;left:50%;transform:translateX(-50%);bottom:36px;background:#071728;padding:12px 16px;border-radius:10px;border:1px solid rgba(255,255,255,0.03);box-shadow:0 8px 24px rgba(2,6,23,0.6);display:flex;gap:10px;align-items:center}
@media (max-width:980px){
.container{grid-template-columns:1fr;min-height:720px}
.aside{order:2}
.main{order:1}
.search{width:100%}
} </style>
</head>
<body>
<div class="container" id="app">
<aside class="aside">
<div class="brand">
<div class="logo">WV</div>
<div>
<div class="title">Value Web Demo</div>
<div class="subtitle">Mini project • interactive</div>
</div>
</div>
<div class="metrics">
<div class="metric">
<strong id="stat-cards">7</strong>
Cards
</div>
<div class="metric">
<strong id="stat-users">1</strong>
Sessions
</div>
</div>
<div class="preview">
<div class="card">
<div class="icon">JS</div>
<div class="info">
<h4 id="card-title">Value Converter</h4>
<p id="card-desc">Convert units, focus on precision and UX.</p>
</div>
<div class="right">
<div class="badge" id="card-status">Ready</div>
</div>
</div>
</div>
<div class="controls">
<button class="btn" id="resetBtn">Reset</button>
<button class="btn" id="exportBtn">Export</button>
</div>
<div style="font-size:12px;color:var(--muted);margin-top:6px">Local demo • data stored in your browser</div>
</aside>
<main class="main">
<div class="topbar">
<div class="controls-row">
<div class="search">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden><path d="M21 21l-4.35-4.35" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><circle cx="11" cy="11" r="6" stroke="currentColor" stroke-width="1.5"></circle></svg>
<input id="q" placeholder="Search cards, examples..." />
</div>
<div style="display:flex;gap:8px">
<select id="preset">
<option value="length">Length</option>
<option value="weight">Weight</option>
<option value="temperature">Temperature</option>
<option value="currency">Currency (demo)</option>
</select>
<button class="btn small" id="addCard">Add</button>
</div>
</div>
<div class="controls-row">
<div class="badge" id="modeLabel">Demo Mode</div>
<button class="btn" id="themeToggle">Toggle</button>
</div>
</div>
```
<div class="grid">
<div class="panel">
<div class="hero">
<div class="hero-left">
<h1 id="page-title">Interactive Value Conversion</h1>
<p>Quickly convert between units with precision and preview results live. Try adding cards and running comparisons.</p>
<div style="margin-top:12px;display:flex;gap:8px">
<button class="play" id="runBtn">Open Converter</button>
<button class="btn" id="demoExample">Example</button>
</div>
</div>
<div style="width:36%;text-align:right">
<div class="badge" id="liveRate">Rates: local</div>
<div style="margin-top:12px;color:var(--muted);font-size:13px">Last saved: <span id="lastSaved">never</span></div>
</div>
</div>
<div class="list" id="cardList">
</div>
</div>
<div class="panel">
<div style="display:flex;justify-content:space-between;align-items:center">
<div>
<h3 style="margin:0">Converter</h3>
<div style="color:var(--muted);font-size:13px;margin-top:6px">Select a card and convert values instantly</div>
</div>
<div class="badge" id="selLabel">No card</div>
</div>
<div style="margin-top:12px">
<div class="form-row" style="margin-bottom:8px">
<input type="text" id="valueInput" placeholder="Enter value" />
<select id="fromUnit"></select>
<select id="toUnit"></select>
</div>
<div class="panel" id="resultPanel" style="margin-top:12px;padding:12px;background:linear-gradient(180deg,rgba(255,255,255,0.02),transparent);display:flex;justify-content:space-between;align-items:center">
<div>
<div style="font-size:13px;color:var(--muted)">Result</div>
<div id="result" style="font-size:18px;font-weight:700">—</div>
</div>
<div>
<button class="btn small" id="copyBtn">Copy</button>
<button class="btn small" id="swapBtn">Swap</button>
</div>
</div>
</div>
<div class="footer">
<div id="helpText">Tip: Use presets to add common unit groups</div>
<div>
<button class="btn small" id="saveBtn">Save</button>
<button class="btn small" id="clearBtn">Clear</button>
</div>
</div>
</div>
</div>
<div style="display:flex;justify-content:space-between;gap:12px">
<div style="flex:1" class="panel" id="notesPanel">
<h4 style="margin:0 0 8px 0">Notes</h4>
<textarea id="notes" placeholder="Write quick notes..."></textarea>
</div>
<div style="width:300px" class="panel">
<h4 style="margin:0 0 8px 0">Activity</h4>
<div id="activity" style="display:flex;flex-direction:column;gap:8px"></div>
</div>
</div>
```
</main>
</div>
<div id="toast" class="toast" style="display:none">
<div id="toastText">Saved</div>
</div>
<script>
const presets = {
length: { units:[{k:'m',n:'Meters'},{k:'km',n:'Kilometers'},{k:'cm',n:'Centimeters'},{k:'inch',n:'Inches'},{k:'ft',n:'Feet'}], convert:(v,f,t)=>{const factors={m:1,km:1000,cm:0.01,inch:0.0254,ft:0.3048};return v* factors[f]/factors[t]}},
weight: { units:[{k:'kg',n:'Kilograms'},{k:'g',n:'Grams'},{k:'lb',n:'Pounds'},{k:'oz',n:'Ounces'}], convert:(v,f,t)=>{const factors={kg:1,g:0.001,lb:0.45359237,oz:0.028349523125};return v* factors[f]/factors[t]}},
temperature: { units:[{k:'c',n:'Celsius'},{k:'f',n:'Fahrenheit'},{k:'k',n:'Kelvin'}], convert:(v,f,t)=>{if(f===t) return v; if(f==='c'&&t==='f') return v*9/5+32; if(f==='f'&&t==='c') return (v-32)*5/9; if(f==='c'&&t==='k') return v+273.15; if(f==='k'&&t==='c') return v-273.15; if(f==='f'&&t==='k') return (v-32)*5/9+273.15; if(f==='k'&&t==='f') return (v-273.15)*9/5+32}},
currency: { units:[{k:'USD',n:'USD'},{k:'EUR',n:'EUR'},{k:'JPY',n:'JPY'},{k:'INR',n:'INR'}], convert:(v,f,t,rate)=>{const r=rate||{USD:1,EUR:0.93,JPY:160,INR:83};return v*(r[f]/r[t])}}
};
let cards = JSON.parse(localStorage.getItem('demo.cards')||'[]');
let activity = JSON.parse(localStorage.getItem('demo.act')||'[]');
let selected = null;
let rates = {USD:1,EUR:0.93,JPY:160,INR:83};
const el = id=>document.getElementById(id);
const cardList = el('cardList');
const selLabel = el('selLabel');
const fromUnit = el('fromUnit');
const toUnit = el('toUnit');
const result = el('result');
const valueInput = el('valueInput');
const q = el('q');
const preset = el('preset');
function renderCards(filter=''){
cardList.innerHTML='';
const items = cards.filter(c=>c.title.toLowerCase().includes(filter.toLowerCase()));
el('stat-cards').textContent=cards.length;
for(let i=0;i<items.length;i++){
const c=items[i];
const node=document.createElement('div');
node.className='item';
node.innerHTML=`<div style="width:48px;height:48px;border-radius:8px;background:linear-gradient(135deg,rgba(124,92,255,0.12),rgba(90,208,255,0.06));display:flex;align-items:center;justify-content:center;font-weight:700;color:var(--accent)">${c.icon||'U'}</div>
<div style="flex:1"><h4>${c.title}</h4><p>${c.desc}</p><div class="progress"><b style="width:${Math.min(100,c.progress)}%"></b></div></div>
<div style="display:flex;flex-direction:column;gap:8px">
<button class="btn small" data-id="${c.id}" data-action="select">Open</button>
<button class="btn small" data-id="${c.id}" data-action="delete">Del</button>
</div>`;
cardList.appendChild(node);
}
attachCardButtons();
}
function attachCardButtons(){
cardList.querySelectorAll('button').forEach(b=>{
b.onclick=()=>{
const id=b.dataset.id;
const action=b.dataset.action;
if(action==='select') openCard(id);
if(action==='delete') { cards=cards.filter(x=>x.id!==id); save(); renderCards(q.value); log('Deleted card'); }
};
});
}
function openCard(id){
selected = cards.find(c=>c.id===id);
if(!selected) return;
selLabel.textContent=selected.title;
populateUnits(selected.units);
el('card-title').textContent=selected.title;
el('card-desc').textContent=selected.desc;
el('card-status').textContent='Loaded';
el('lastSaved').textContent=new Date(selected.savedAt).toLocaleString();
populateNotes(selected.notes||'');
}
function populateUnits(list){
fromUnit.innerHTML='';toUnit.innerHTML='';
list.forEach(u=>{
const o1=document.createElement('option');o1.value=u.k;o1.textContent=u.n;
const o2=o1.cloneNode(true);
fromUnit.appendChild(o1);toUnit.appendChild(o2);
});
}
function populateNotes(t){el('notes').value=t}
function addCardFromPreset(key){
const p=presets[key];
if(!p) return;
const id='c_'+Date.now().toString(36);
const card={id,title: key.charAt(0).toUpperCase()+key.slice(1)+' Units',desc:`Common ${key} units`,units:p.units,icon:key[0].toUpperCase(),progress:Math.floor(Math.random()*60)+20,savedAt:Date.now(),notes:''};
cards.unshift(card);
save();
renderCards(q.value);
log('Added preset: '+key);
openCard(card.id);
}
function convertValue(){
if(!selected) return;
const v=parseFloat(valueInput.value);
if(Number.isNaN(v)) { result.textContent='—'; return; }
const from=fromUnit.value; const to=toUnit.value;
const presetKey = getPresetFor(selected);
let out='—';
if(presetKey && presetKey!=='currency'){
out = presets[presetKey].convert(v,from,to);
} else if(presetKey==='currency'){
out = presets.currency.convert(v,from,to,rates);
} else {
const idxFrom = selected.units.find(u=>u.k===from);
const idxTo = selected.units.find(u=>u.k===to);
if(idxFrom && idxTo) out=(v*1);
}
if(typeof out==='number') out = Number(out.toFixed(6)).toString();
result.textContent=out;
}
function getPresetFor(card){
const title = card.title.toLowerCase();
if(title.includes('length')) return 'length';
if(title.includes('weight')) return 'weight';
if(title.includes('temperature')) return 'temperature';
if(title.includes('currency')) return 'currency';
return null;
}
function save(){
localStorage.setItem('demo.cards',JSON.stringify(cards));
localStorage.setItem('demo.act',JSON.stringify(activity));
el('lastSaved').textContent=new Date().toLocaleString();
}
function log(text){
activity.unshift({t:Date.now(),text});
if(activity.length>10) activity.pop();
renderActivity();
save();
showToast(text);
}
function renderActivity(){
const container = el('activity');
container.innerHTML='';
activity.forEach(a=>{
const d=document.createElement('div');
d.style.fontSize='13px'; d.style.color='var(--muted)';
d.textContent = `${new Date(a.t).toLocaleTimeString()} • ${a.text}`;
container.appendChild(d);
});
}
function showToast(text,ms=1400){
el('toastText').textContent=text;
const t=el('toast'); t.style.display='flex'; clearTimeout(t._hide);
t._hide=setTimeout(()=>t.style.display='none',ms);
}
function exportData(){
const data = {cards,activity};
const blob = new Blob([JSON.stringify(data,0,2)],{type:'application/json'});
const url = URL.createObjectURL(blob);
const a=document.createElement('a'); a.href=url; a.download='demo-data.json'; a.click();
URL.revokeObjectURL(url);
log('Exported data');
}
function clearAll(){
cards=[]; activity=[]; selected=null;
save(); renderCards(); renderActivity();
el('card-title').textContent='Value Converter';
el('card-desc').textContent='Convert units, focus on precision and UX.';
selLabel.textContent='No card';
populateUnits([{k:'',n:'--'},{k:'',n:'--'}]);
result.textContent='—';
showToast('Cleared');
}
function restoreExample(){
cards = [
{id:'c_len',title:'Length Units',desc:'Meters, Kilometers, Inches',units:presets.length.units,icon:'L',progress:45,savedAt:Date.now(),notes:'Used for distance conversion'},
{id:'c_temp',title:'Temperature Units',desc:'Celsius, Fahrenheit, Kelvin',units:presets.temperature.units,icon:'T',progress:65,savedAt:Date.now(),notes:'Careful with offsets'}
];
save(); renderCards(); log('Loaded example cards');
openCard(cards[0].id);
}
function init(){
renderCards();
renderActivity();
if(cards.length===0) restoreExample();
q.oninput=()=>renderCards(q.value);
el('addCard').onclick=()=>addCardFromPreset(preset.value);
el('runBtn').onclick=()=>{ if(selected) showToast('Converter opened'); else showToast('Select a card'); };
el('demoExample').onclick=restoreExample;
el('resetBtn').onclick=()=>{ clearAll(); };
el('exportBtn').onclick=exportData;
el('saveBtn').onclick=()=>{
if(!selected){ showToast('Select a card first'); return; }
selected.notes = el('notes').value;
selected.savedAt = Date.now();
save();
renderCards(q.value);
log('Saved card');
};
el('themeToggle').onclick=()=>{ document.body.classList.toggle('alt'); showToast('Toggled theme'); };
el('clearBtn').onclick=()=>{ el('notes').value=''; showToast('Notes cleared'); };
el('copyBtn').onclick=()=>{ navigator.clipboard.writeText(result.textContent).then(()=>showToast('Copied')); };
el('swapBtn').onclick=()=>{ const a=fromUnit.value; fromUnit.value=toUnit.value; toUnit.value=a; convertValue(); };
valueInput.oninput=convertValue;
fromUnit.onchange=convertValue;
toUnit.onchange=convertValue;
el('preset').onchange=()=>{};
el('q').value='';
el('stat-users').textContent=1;
el('liveRate').textContent='Rates: local';
el('lastSaved').textContent= cards[0]? new Date(cards[0].savedAt).toLocaleString() : 'never';
el('notes').oninput=()=>{};
el('notes').value='';
el('cardList').addEventListener('click',()=>{});
document.addEventListener('keydown',(e)=>{ if(e.key==='k'&& (e.ctrlKey||e.metaKey)) { e.preventDefault(); q.focus(); }});
}
init();
</script>
</body>
</html>
::contentReference[oaicite:0]{index=0}