| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"/> |
| <meta name="viewport" content="width=device-width,initial-scale=1"/> |
| <title>DropVault Β· Instant File Share</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=Instrument+Sans:ital,wght@0,400;0,500;1,400&display=swap" rel="stylesheet"> |
| <style> |
| :root { |
| --bg: #f5f2eb; |
| --ink: #1a1814; |
| --muted: #8a8478; |
| --accent: #d4481a; |
| --accent2: #2a5caa; |
| --card: #ffffff; |
| --border: #ddd9d0; |
| --success: #2a7a4b; |
| --warn: #c07a1a; |
| } |
| |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| |
| body { |
| font-family: 'Instrument Sans', sans-serif; |
| background: var(--bg); |
| color: var(--ink); |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| padding: 3rem 1.5rem 5rem; |
| background-image: |
| radial-gradient(circle at 10% 20%, rgba(212,72,26,0.06) 0%, transparent 50%), |
| radial-gradient(circle at 90% 80%, rgba(42,92,170,0.06) 0%, transparent 50%); |
| } |
| |
| |
| header { |
| text-align: center; |
| margin-bottom: 3.5rem; |
| animation: fadeDown 0.6s ease both; |
| } |
| |
| .logo { |
| font-family: 'Syne', sans-serif; |
| font-weight: 800; |
| font-size: clamp(2.4rem, 6vw, 3.8rem); |
| letter-spacing: -0.04em; |
| color: var(--ink); |
| line-height: 1; |
| } |
| .logo span { color: var(--accent); } |
| |
| .tagline { |
| margin-top: 0.5rem; |
| font-size: 0.85rem; |
| color: var(--muted); |
| letter-spacing: 0.18em; |
| text-transform: uppercase; |
| font-weight: 500; |
| } |
| |
| |
| .drop-zone { |
| width: min(580px, 100%); |
| background: var(--card); |
| border: 2px dashed var(--border); |
| border-radius: 1.4rem; |
| padding: 3.5rem 2rem; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 1.1rem; |
| cursor: pointer; |
| transition: border-color 0.2s, background 0.2s, transform 0.15s; |
| animation: fadeUp 0.6s 0.1s ease both; |
| position: relative; |
| overflow: hidden; |
| } |
| .drop-zone::before { |
| content: ''; |
| position: absolute; |
| inset: 0; |
| background: radial-gradient(circle at 50% 0%, rgba(212,72,26,0.04), transparent 60%); |
| pointer-events: none; |
| } |
| .drop-zone:hover, .drop-zone.drag-over { |
| border-color: var(--accent); |
| background: #fff8f6; |
| transform: translateY(-2px); |
| } |
| .drop-zone.drag-over { border-style: solid; } |
| |
| .drop-icon { |
| width: 4.5rem; |
| height: 4.5rem; |
| background: linear-gradient(135deg, #fff0ec, #fde4db); |
| border-radius: 1.2rem; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 2rem; |
| transition: transform 0.2s; |
| border: 1.5px solid rgba(212,72,26,0.15); |
| } |
| .drop-zone:hover .drop-icon { transform: scale(1.08) rotate(-4deg); } |
| |
| .drop-text { |
| font-family: 'Syne', sans-serif; |
| font-size: 1.1rem; |
| font-weight: 600; |
| color: var(--ink); |
| } |
| .drop-sub { |
| font-size: 0.8rem; |
| color: var(--muted); |
| text-align: center; |
| line-height: 1.6; |
| } |
| |
| .browse-btn { |
| background: var(--accent); |
| color: white; |
| border: none; |
| padding: 0.65rem 1.8rem; |
| border-radius: 2rem; |
| font-family: 'Syne', sans-serif; |
| font-weight: 600; |
| font-size: 0.88rem; |
| cursor: pointer; |
| letter-spacing: 0.02em; |
| transition: background 0.15s, transform 0.1s; |
| } |
| .browse-btn:hover { background: #bc3e14; transform: scale(1.03); } |
| .browse-btn:active { transform: scale(0.98); } |
| |
| #fileInput { display: none; } |
| |
| |
| .progress-wrap { |
| width: min(580px, 100%); |
| display: none; |
| flex-direction: column; |
| gap: 0.6rem; |
| animation: fadeUp 0.4s ease both; |
| } |
| .progress-wrap.show { display: flex; } |
| .progress-label { |
| font-size: 0.82rem; |
| color: var(--muted); |
| display: flex; |
| justify-content: space-between; |
| } |
| .progress-bar-bg { |
| height: 6px; |
| background: var(--border); |
| border-radius: 99px; |
| overflow: hidden; |
| } |
| .progress-bar { |
| height: 100%; |
| background: linear-gradient(90deg, var(--accent), #e8703a); |
| border-radius: 99px; |
| width: 0%; |
| transition: width 0.3s ease; |
| } |
| |
| |
| .result-card { |
| width: min(580px, 100%); |
| background: var(--card); |
| border: 1.5px solid var(--border); |
| border-radius: 1.4rem; |
| padding: 1.8rem; |
| display: none; |
| flex-direction: column; |
| gap: 1.2rem; |
| animation: fadeUp 0.5s ease both; |
| position: relative; |
| overflow: hidden; |
| } |
| .result-card::before { |
| content: ''; |
| position: absolute; |
| top: 0; left: 0; right: 0; |
| height: 3px; |
| background: linear-gradient(90deg, var(--accent), var(--accent2)); |
| } |
| .result-card.show { display: flex; } |
| |
| .result-header { |
| display: flex; |
| align-items: center; |
| gap: 0.8rem; |
| } |
| .result-icon { |
| width: 2.8rem; |
| height: 2.8rem; |
| background: #eef6f1; |
| border-radius: 0.8rem; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 1.3rem; |
| flex-shrink: 0; |
| } |
| .result-name { |
| font-family: 'Syne', sans-serif; |
| font-weight: 700; |
| font-size: 1rem; |
| word-break: break-all; |
| } |
| .result-meta { font-size: 0.78rem; color: var(--muted); margin-top: 0.1rem; } |
| |
| .link-row { |
| display: flex; |
| gap: 0.6rem; |
| align-items: center; |
| } |
| .link-box { |
| flex: 1; |
| background: #f7f5f0; |
| border: 1.5px solid var(--border); |
| border-radius: 0.7rem; |
| padding: 0.65rem 0.9rem; |
| font-size: 0.82rem; |
| color: var(--accent2); |
| word-break: break-all; |
| font-family: 'Instrument Sans', monospace; |
| } |
| .copy-btn { |
| background: var(--ink); |
| color: white; |
| border: none; |
| padding: 0.65rem 1.2rem; |
| border-radius: 0.7rem; |
| font-family: 'Syne', sans-serif; |
| font-weight: 600; |
| font-size: 0.8rem; |
| cursor: pointer; |
| white-space: nowrap; |
| transition: background 0.15s, transform 0.1s; |
| flex-shrink: 0; |
| } |
| .copy-btn:hover { background: #333; } |
| .copy-btn.copied { background: var(--success); } |
| |
| |
| .timer-row { |
| display: flex; |
| align-items: center; |
| gap: 0.7rem; |
| } |
| .timer-label { |
| font-size: 0.78rem; |
| color: var(--muted); |
| white-space: nowrap; |
| } |
| .timer-bar-bg { |
| flex: 1; |
| height: 5px; |
| background: var(--border); |
| border-radius: 99px; |
| overflow: hidden; |
| } |
| .timer-bar { |
| height: 100%; |
| background: linear-gradient(90deg, var(--success), #6fcf97); |
| border-radius: 99px; |
| width: 100%; |
| transition: width 1s linear, background 1s; |
| } |
| .timer-count { |
| font-family: 'Syne', sans-serif; |
| font-weight: 700; |
| font-size: 0.85rem; |
| color: var(--ink); |
| min-width: 2.5rem; |
| text-align: right; |
| } |
| |
| .download-btn { |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| gap: 0.5rem; |
| background: transparent; |
| color: var(--accent2); |
| border: 1.5px solid var(--accent2); |
| padding: 0.65rem 1.4rem; |
| border-radius: 0.7rem; |
| font-family: 'Syne', sans-serif; |
| font-weight: 600; |
| font-size: 0.85rem; |
| cursor: pointer; |
| text-decoration: none; |
| transition: background 0.15s, color 0.15s; |
| } |
| .download-btn:hover { background: var(--accent2); color: white; } |
| |
| .upload-another { |
| background: none; |
| border: none; |
| color: var(--muted); |
| font-size: 0.8rem; |
| cursor: pointer; |
| text-align: center; |
| font-family: 'Instrument Sans', sans-serif; |
| text-decoration: underline; |
| transition: color 0.15s; |
| } |
| .upload-another:hover { color: var(--ink); } |
| |
| |
| .steps { |
| width: min(580px, 100%); |
| margin-top: 2.5rem; |
| display: grid; |
| grid-template-columns: repeat(3, 1fr); |
| gap: 1rem; |
| animation: fadeUp 0.6s 0.25s ease both; |
| } |
| .step { |
| background: var(--card); |
| border: 1.5px solid var(--border); |
| border-radius: 1rem; |
| padding: 1.2rem 1rem; |
| text-align: center; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 0.5rem; |
| } |
| .step-num { |
| font-family: 'Syne', sans-serif; |
| font-weight: 800; |
| font-size: 1.5rem; |
| color: var(--border); |
| line-height: 1; |
| } |
| .step-title { |
| font-family: 'Syne', sans-serif; |
| font-weight: 600; |
| font-size: 0.85rem; |
| } |
| .step-desc { font-size: 0.75rem; color: var(--muted); line-height: 1.5; } |
| |
| @media (max-width: 480px) { |
| .steps { grid-template-columns: 1fr; } |
| .link-row { flex-direction: column; } |
| .copy-btn { width: 100%; } |
| } |
| |
| |
| @keyframes fadeDown { |
| from { opacity: 0; transform: translateY(-18px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| @keyframes fadeUp { |
| from { opacity: 0; transform: translateY(18px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| |
| .toast { |
| position: fixed; |
| bottom: 2rem; |
| left: 50%; |
| transform: translateX(-50%) translateY(4rem); |
| background: var(--ink); |
| color: white; |
| padding: 0.7rem 1.5rem; |
| border-radius: 2rem; |
| font-size: 0.85rem; |
| font-family: 'Syne', sans-serif; |
| font-weight: 600; |
| transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1); |
| pointer-events: none; |
| z-index: 100; |
| white-space: nowrap; |
| } |
| .toast.show { transform: translateX(-50%) translateY(0); } |
| </style> |
| </head> |
| <body> |
|
|
| <header> |
| <div class="logo">Drop<span>Vault</span></div> |
| <div class="tagline">Instant Β· Secure Β· 5-Minute Links</div> |
| </header> |
|
|
| |
| <div class="drop-zone" id="dropZone"> |
| <div class="drop-icon">π¦</div> |
| <div class="drop-text">Drop your file here</div> |
| <div class="drop-sub">Any file type supported<br>Link auto-expires in 5 minutes</div> |
| <button class="browse-btn" onclick="document.getElementById('fileInput').click()">Browse Files</button> |
| <input type="file" id="fileInput"/> |
| </div> |
|
|
| |
| <div class="progress-wrap" id="progressWrap" style="margin-top:1rem"> |
| <div class="progress-label"> |
| <span id="progressFile">Uploadingβ¦</span> |
| <span id="progressPct">0%</span> |
| </div> |
| <div class="progress-bar-bg"> |
| <div class="progress-bar" id="progressBar"></div> |
| </div> |
| </div> |
|
|
| |
| <div class="result-card" id="resultCard" style="margin-top:1rem"> |
| <div class="result-header"> |
| <div class="result-icon">β
</div> |
| <div> |
| <div class="result-name" id="resultName">filename.txt</div> |
| <div class="result-meta">Ready to share Β· expires in <span id="metaTimer">5:00</span></div> |
| </div> |
| </div> |
|
|
| <div class="link-row"> |
| <div class="link-box" id="linkBox">β</div> |
| <button class="copy-btn" id="copyBtn" onclick="copyLink()">Copy</button> |
| </div> |
|
|
| <div class="timer-row"> |
| <span class="timer-label">Expires in</span> |
| <div class="timer-bar-bg"> |
| <div class="timer-bar" id="timerBar"></div> |
| </div> |
| <span class="timer-count" id="timerCount">5:00</span> |
| </div> |
|
|
| <a class="download-btn" id="downloadBtn" href="#" target="_blank">β¬ Download File</a> |
|
|
| <button class="upload-another" onclick="resetUI()">Upload another file</button> |
| </div> |
|
|
| |
| <div class="steps" id="steps"> |
| <div class="step"> |
| <div class="step-num">01</div> |
| <div class="step-title">Upload</div> |
| <div class="step-desc">Drop or browse any file β no account needed</div> |
| </div> |
| <div class="step"> |
| <div class="step-num">02</div> |
| <div class="step-title">Share</div> |
| <div class="step-desc">Copy the link and send it to anyone instantly</div> |
| </div> |
| <div class="step"> |
| <div class="step-num">03</div> |
| <div class="step-title">Auto-Delete</div> |
| <div class="step-desc">File vanishes after 5 minutes β zero clutter</div> |
| </div> |
| </div> |
|
|
| |
| <div class="toast" id="toast">Link copied!</div> |
|
|
| <script> |
| const dropZone = document.getElementById('dropZone'); |
| const fileInput = document.getElementById('fileInput'); |
| const progressWrap = document.getElementById('progressWrap'); |
| const progressBar = document.getElementById('progressBar'); |
| const progressPct = document.getElementById('progressPct'); |
| const progressFile = document.getElementById('progressFile'); |
| const resultCard = document.getElementById('resultCard'); |
| const resultName = document.getElementById('resultName'); |
| const linkBox = document.getElementById('linkBox'); |
| const copyBtn = document.getElementById('copyBtn'); |
| const downloadBtn= document.getElementById('downloadBtn'); |
| const timerBar = document.getElementById('timerBar'); |
| const timerCount = document.getElementById('timerCount'); |
| const metaTimer = document.getElementById('metaTimer'); |
| const steps = document.getElementById('steps'); |
| const toast = document.getElementById('toast'); |
| |
| let timerInterval = null; |
| |
| |
| dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('drag-over'); }); |
| dropZone.addEventListener('dragleave', () => dropZone.classList.remove('drag-over')); |
| dropZone.addEventListener('drop', e => { |
| e.preventDefault(); |
| dropZone.classList.remove('drag-over'); |
| const file = e.dataTransfer.files[0]; |
| if (file) uploadFile(file); |
| }); |
| |
| dropZone.addEventListener('click', e => { |
| if (e.target.classList.contains('browse-btn')) return; |
| fileInput.click(); |
| }); |
| |
| fileInput.addEventListener('change', () => { |
| if (fileInput.files[0]) uploadFile(fileInput.files[0]); |
| }); |
| |
| |
| function uploadFile(file) { |
| dropZone.style.display = 'none'; |
| steps.style.display = 'none'; |
| progressWrap.classList.add('show'); |
| progressFile.textContent = file.name; |
| progressBar.style.width = '0%'; |
| progressPct.textContent = '0%'; |
| |
| const form = new FormData(); |
| form.append('file', file); |
| |
| const xhr = new XMLHttpRequest(); |
| xhr.open('POST', '/upload'); |
| |
| xhr.upload.onprogress = e => { |
| if (e.lengthComputable) { |
| const pct = Math.round(e.loaded / e.total * 100); |
| progressBar.style.width = pct + '%'; |
| progressPct.textContent = pct + '%'; |
| } |
| }; |
| |
| xhr.onload = () => { |
| progressWrap.classList.remove('show'); |
| if (xhr.status === 200) { |
| const data = JSON.parse(xhr.responseText); |
| showResult(data, file.name); |
| } else { |
| alert('Upload failed. Please try again.'); |
| resetUI(); |
| } |
| }; |
| |
| xhr.onerror = () => { alert('Network error.'); resetUI(); }; |
| xhr.send(form); |
| } |
| |
| |
| function showResult(data, originalName) { |
| const fullLink = window.location.origin + data.link; |
| resultName.textContent = originalName; |
| linkBox.textContent = fullLink; |
| downloadBtn.href = data.link; |
| resultCard.classList.add('show'); |
| startTimer(300); |
| } |
| |
| |
| function startTimer(seconds) { |
| if (timerInterval) clearInterval(timerInterval); |
| let remaining = seconds; |
| |
| function tick() { |
| const m = Math.floor(remaining / 60); |
| const s = remaining % 60; |
| const label = `${m}:${s.toString().padStart(2, '0')}`; |
| timerCount.textContent = label; |
| metaTimer.textContent = label; |
| |
| const pct = (remaining / 300) * 100; |
| timerBar.style.width = pct + '%'; |
| |
| |
| if (pct > 50) { |
| timerBar.style.background = 'linear-gradient(90deg,#2a7a4b,#6fcf97)'; |
| } else if (pct > 20) { |
| timerBar.style.background = 'linear-gradient(90deg,#c07a1a,#f4a742)'; |
| } else { |
| timerBar.style.background = 'linear-gradient(90deg,#d4481a,#f07050)'; |
| } |
| |
| if (remaining <= 0) { |
| clearInterval(timerInterval); |
| timerCount.textContent = 'Expired'; |
| linkBox.textContent = 'Link has expired'; |
| downloadBtn.style.opacity = '0.4'; |
| downloadBtn.style.pointerEvents = 'none'; |
| copyBtn.style.opacity = '0.4'; |
| copyBtn.style.pointerEvents = 'none'; |
| } |
| remaining--; |
| } |
| |
| tick(); |
| timerInterval = setInterval(tick, 1000); |
| } |
| |
| |
| function copyLink() { |
| const link = linkBox.textContent; |
| if (!link || link === 'β' || link.includes('expired')) return; |
| navigator.clipboard.writeText(link).then(() => { |
| copyBtn.textContent = 'Copied!'; |
| copyBtn.classList.add('copied'); |
| showToast('Link copied to clipboard!'); |
| setTimeout(() => { |
| copyBtn.textContent = 'Copy'; |
| copyBtn.classList.remove('copied'); |
| }, 2000); |
| }); |
| } |
| |
| |
| function showToast(msg) { |
| toast.textContent = msg; |
| toast.classList.add('show'); |
| setTimeout(() => toast.classList.remove('show'), 2500); |
| } |
| |
| |
| function resetUI() { |
| if (timerInterval) clearInterval(timerInterval); |
| resultCard.classList.remove('show'); |
| progressWrap.classList.remove('show'); |
| dropZone.style.display = ''; |
| steps.style.display = ''; |
| fileInput.value = ''; |
| progressBar.style.width = '0%'; |
| copyBtn.textContent = 'Copy'; |
| copyBtn.classList.remove('copied'); |
| downloadBtn.style.opacity = ''; |
| downloadBtn.style.pointerEvents = ''; |
| copyBtn.style.opacity = ''; |
| copyBtn.style.pointerEvents = ''; |
| timerBar.style.width = '100%'; |
| } |
| </script> |
| </body> |
| </html> |