JustPaste
HomeCategoriesAboutDonateContactTerms of UsePrivacy Policy
JustPaste

Free online notepad — write and share instantly

Navigate

  • Home
  • Timeline
  • Categories

Info

  • About
  • Donate
  • Contact

Legal

  • Terms of Use
  • Privacy Policy

© 2026 JustPaste.app. All rights reserved.

Made with ♥ by JustPaste

Untitled Page | JustPaste.app
14 days ago6 views
💻Technology
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Login & Register</title>
  <link rel="stylesheet" href="styles.css" />
  <link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet" />
</head>
<body>
  <div class="bg-grid"></div>

  <main class="container">
    <!-- ========== LOGIN CARD ========== -->
    <div id="login-card" class="card card--login active">
      <div class="card__accent"></div>
      <div class="card__body">
        <div class="card__badge">US_UI_001</div>
        <h1 class="card__title">Login<span class="blink">_</span></h1>
        <p class="card__subtitle">Welcome back. Enter your credentials.</p>

        <form id="login-form" novalidate>
          <div class="input-group">
            <label for="login-username">Username</label>
            <input type="text" id="login-username" placeholder="your_username" autocomplete="username" />
            <span class="error-msg" id="login-username-err"></span>
          </div>

          <div class="input-group">
            <label for="login-password">Password</label>
            <div class="password-wrapper">
              <input type="password" id="login-password" placeholder="••••••••" autocomplete="current-password" />
              <button type="button" class="toggle-pw" data-target="login-password" aria-label="Toggle password visibility">👁</button>
            </div>
            <span class="error-msg" id="login-password-err"></span>
          </div>

          <button type="submit" class="btn btn--primary">Log In →</button>
        </form>

        <p class="switch-text">
          Not registered?
          <button type="button" class="link-btn" id="go-register">Create Account</button>
        </p>
      </div>
    </div>

    <!-- ========== REGISTER CARD ========== -->
    <div id="register-card" class="card card--register">
      <div class="card__accent card__accent--green"></div>
      <div class="card__body">
        <div class="card__badge badge--green">NEW USER</div>
        <h1 class="card__title">Register<span class="blink">_</span></h1>
        <p class="card__subtitle">Create your account in seconds.</p>

        <form id="register-form" novalidate>
          <div class="input-group">
            <label for="reg-username">Username</label>
            <input type="text" id="reg-username" placeholder="pick_a_username" autocomplete="username" />
            <span class="error-msg" id="reg-username-err"></span>
          </div>

          <div class="input-group">
            <label for="reg-email">Email</label>
            <input type="email" id="reg-email" placeholder="[email protected]" autocomplete="email" />
            <span class="error-msg" id="reg-email-err"></span>
          </div>

          <div class="input-group">
            <label for="reg-password">Password</label>
            <div class="password-wrapper">
              <input type="password" id="reg-password" placeholder="••••••••" autocomplete="new-password" />
              <button type="button" class="toggle-pw" data-target="reg-password" aria-label="Toggle password visibility">👁</button>
            </div>
            <span class="error-msg" id="reg-password-err"></span>
          </div>

          <div class="input-group">
            <label for="reg-confirm">Confirm Password</label>
            <div class="password-wrapper">
              <input type="password" id="reg-confirm" placeholder="••••••••" autocomplete="new-password" />
              <button type="button" class="toggle-pw" data-target="reg-confirm" aria-label="Toggle password visibility">👁</button>
            </div>
            <span class="error-msg" id="reg-confirm-err"></span>
          </div>

          <button type="submit" class="btn btn--green">Register →</button>
        </form>

        <p class="switch-text">
          Already have an account?
          <button type="button" class="link-btn" id="go-login">Log In</button>
        </p>
      </div>
    </div>

    <!-- ========== SUCCESS TOAST ========== -->
    <div id="toast" class="toast" aria-live="polite"></div>
  </main>

  <script src="script.js"></script>
</body>
</html>


/* ===== RESET & BASE ===== */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --black: #1a1a1a;
  --white: #fffdf7;
  --yellow: #facc15;
  --pink: #f472b6;
  --blue: #3b82f6;
  --green: #22c55e;
  --red: #ef4444;
  --orange: #fb923c;
  --purple: #a855f7;
  --border: 3px solid var(--black);
  --shadow: 6px 6px 0 var(--black);
  --shadow-sm: 4px 4px 0 var(--black);
  --shadow-lg: 8px 8px 0 var(--black);
  --radius: 0px;
  --font-mono: "Space Mono", monospace;
  --font-sans: "Space Grotesk", system-ui, sans-serif;
}

html {
  font-size: 16px;
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-sans);
  color: var(--black);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: relative;
}

/* ===== ANIMATED GRADIENT BACKGROUND ===== */
.bg-gradient {
  position: fixed;
  inset: 0;
  background: linear-gradient(
    135deg,
    #0f172a 0%,
    #1e3a5f 20%,
    #1e40af 40%,
    #7c3aed 60%,
    #db2777 80%,
    #f97316 100%
  );
  background-size: 400% 400%;
  animation: gradientShift 12s ease infinite;
  z-index: 0;
}

@keyframes gradientShift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* ===== GRID OVERLAY ===== */
.bg-grid {
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
  background-size: 40px 40px;
  pointer-events: none;
  z-index: 1;
}

/* ===== NOISE TEXTURE ===== */
.bg-noise {
  position: fixed;
  inset: 0;
  opacity: 0.35;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  pointer-events: none;
  z-index: 2;
}

/* ===== FLOATING DECORATIONS ===== */
.floater {
  position: fixed;
  font-size: 2.5rem;
  opacity: 0.15;
  z-index: 2;
  pointer-events: none;
  animation: floatUp 18s linear infinite;
}

.floater--1 { left: 8%;  top: 20%; animation-duration: 20s; font-size: 3.5rem; opacity: 0.2; }
.floater--2 { left: 85%; top: 15%; animation-duration: 15s; animation-delay: -3s; }
.floater--3 { left: 75%; top: 70%; animation-duration: 22s; animation-delay: -7s; }
.floater--4 { left: 12%; top: 75%; animation-duration: 17s; animation-delay: -5s; font-size: 3rem; }
.floater--5 { left: 50%; top: 40%; animation-duration: 25s; animation-delay: -10s; }

@keyframes floatUp {
  0%   { transform: translateY(0) rotate(0deg); }
  25%  { transform: translateY(-30px) rotate(5deg); }
  50%  { transform: translateY(-10px) rotate(-3deg); }
  75%  { transform: translateY(-40px) rotate(4deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

/* ===== SCROLLING TRAIN TRACK ===== */
.track-strip {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  overflow: hidden;
  z-index: 3;
  background: rgba(0, 0, 0, 0.5);
  border-top: 3px solid rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(4px);
  padding: 0.4rem 0;
}

.track-scroll {
  display: flex;
  white-space: nowrap;
  animation: scrollTrack 12s linear infinite;
  font-family: var(--font-mono);
  font-size: 1rem;
  color: rgba(250, 204, 21, 0.6);
  letter-spacing: 2px;
}

.track-scroll span {
  flex-shrink: 0;
  padding-right: 1rem;
}

@keyframes scrollTrack {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

/* ===== CONTAINER ===== */
.container {
  position: relative;
  z-index: 10;
  width: 100%;
  max-width: 480px;
  padding: 1.5rem;
}

/* ===== BRAND ===== */
.brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  margin-bottom: 1.25rem;
  animation: popIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.brand__icon {
  font-size: 2rem;
}

.brand__name {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--white);
  text-shadow: 3px 3px 0 var(--black);
  letter-spacing: 0.05em;
}

/* ===== CARD ===== */
.card {
  display: none;
  border: var(--border);
  box-shadow: var(--shadow-lg);
  background: var(--white);
  position: relative;
  overflow: hidden;
  animation: popIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.card.active {
  display: block;
}

@keyframes popIn {
  from {
    opacity: 0;
    transform: translateY(24px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* accent strip */
.card__accent {
  height: 10px;
  background: repeating-linear-gradient(
    90deg,
    var(--yellow) 0px,
    var(--yellow) 20px,
    var(--black) 20px,
    var(--black) 24px
  );
  border-bottom: var(--border);
}

.card__accent--green {
  background: repeating-linear-gradient(
    90deg,
    var(--green) 0px,
    var(--green) 20px,
    var(--black) 20px,
    var(--black) 24px
  );
}

.card__body {
  padding: 2rem 2rem 1.75rem;
}

/* badge row */
.card__header {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.card__badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--yellow);
  border: 2px solid var(--black);
  box-shadow: var(--shadow-sm);
  padding: 0.2rem 0.65rem;
}

.badge--green {
  background: var(--green);
}

.badge--outline {
  background: transparent;
  color: var(--black);
}

/* title */
.card__title {
  font-family: var(--font-mono);
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1.1;
  margin-bottom: 0.3rem;
}

.blink {
  animation: blink 1s step-end infinite;
  color: var(--blue);
}

@keyframes blink {
  50% { opacity: 0; }
}

.card__subtitle {
  font-size: 0.95rem;
  color: #666;
  margin-bottom: 1.75rem;
}

/* ===== INPUT GROUP ===== */
.input-group {
  margin-bottom: 1.15rem;
}

.input-group label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.4rem;
}

.label-icon {
  font-size: 0.85rem;
}

.input-group input {
  width: 100%;
  padding: 0.8rem 1rem;
  font-family: var(--font-sans);
  font-size: 1rem;
  border: var(--border);
  background: #fafaf8;
  box-shadow: var(--shadow-sm);
  outline: none;
  transition: box-shadow 0.2s, transform 0.2s, background 0.2s;
}

.input-group input::placeholder {
  color: #b0b0a8;
}

.input-group input:focus {
  box-shadow: 8px 8px 0 var(--blue);
  transform: translate(-2px, -2px);
  background: var(--white);
}

.input-group input.invalid {
  box-shadow: 6px 6px 0 var(--red);
  border-color: var(--red);
  background: #fff5f5;
}

.input-group input.valid {
  box-shadow: 6px 6px 0 var(--green);
  border-color: var(--green);
  background: #f0fdf4;
}

/* password wrapper */
.password-wrapper {
  position: relative;
}

.password-wrapper input {
  padding-right: 3.2rem;
}

.toggle-pw {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  width: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-left: var(--border);
  cursor: pointer;
  font-size: 1rem;
  transition: background 0.15s;
}

.toggle-pw:hover {
  background: var(--yellow);
}

/* error message */
.error-msg {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--red);
  min-height: 1rem;
  margin-top: 0.3rem;
}

/* ===== BUTTONS ===== */
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.9rem 1.5rem;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border: var(--border);
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: transform 0.12s, box-shadow 0.12s;
  margin-top: 0.75rem;
}

.btn:hover {
  transform: translate(-2px, -2px);
  box-shadow: 8px 8px 0 var(--black);
}

.btn:active {
  transform: translate(4px, 4px);
  box-shadow: 0 0 0 var(--black);
}

.btn__arrow {
  font-size: 1.3rem;
  transition: transform 0.2s;
}

.btn:hover .btn__arrow {
  transform: translateX(4px);
}

.btn--primary {
  background: var(--yellow);
  color: var(--black);
}

.btn--green {
  background: var(--green);
  color: var(--black);
}

/* divider */
.divider {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 1.25rem 0;
}

.divider::before,
.divider::after {
  content: "";
  flex: 1;
  height: 2px;
  background: repeating-linear-gradient(
    90deg,
    var(--black) 0px,
    var(--black) 4px,
    transparent 4px,
    transparent 8px
  );
}

.divider span {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  color: #999;
  letter-spacing: 0.1em;
}

/* switch link */
.switch-text {
  text-align: center;
  font-size: 0.9rem;
  color: #666;
}

.link-btn {
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--blue);
  cursor: pointer;
  text-decoration: none;
  border-bottom: 2px solid var(--blue);
  padding-bottom: 1px;
  transition: color 0.15s, border-color 0.15s;
}

.link-btn:hover {
  color: var(--purple);
  border-color: var(--purple);
}

/* ===== TOAST ===== */
.toast {
  position: fixed;
  bottom: 3rem;
  left: 50%;
  transform: translateX(-50%) translateY(120px);
  padding: 0.85rem 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 700;
  border: var(--border);
  box-shadow: var(--shadow);
  background: var(--green);
  color: var(--black);
  white-space: nowrap;
  z-index: 100;
  opacity: 0;
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s;
}

.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ===== RESPONSIVE ===== */
@media (max-width: 520px) {
  .container {
    padding: 1rem;
  }

  .card__body {
    padding: 1.5rem 1.25rem;
  }

  .card__title {
    font-size: 1.9rem;
  }

  .brand__name {
    font-size: 1.3rem;
  }

  .floater {
    display: none;
  }
}

// ===== DOM REFERENCES =====
const loginCard = document.getElementById("login-card");
const registerCard = document.getElementById("register-card");
const loginForm = document.getElementById("login-form");
const registerForm = document.getElementById("register-form");
const toast = document.getElementById("toast");

// ===== PAGE SWITCHING =====
document.getElementById("go-register").addEventListener("click", () => {
  loginCard.classList.remove("active");
  registerCard.classList.add("active");
  clearForm(loginForm);
});

document.getElementById("go-login").addEventListener("click", () => {
  registerCard.classList.remove("active");
  loginCard.classList.add("active");
  clearForm(registerForm);
});

// ===== PASSWORD TOGGLE =====
document.querySelectorAll(".toggle-pw").forEach((btn) => {
  btn.addEventListener("click", () => {
    const input = document.getElementById(btn.dataset.target);
    const isPassword = input.type === "password";
    input.type = isPassword ? "text" : "password";
    btn.textContent = isPassword ? "🙈" : "👁";
  });
});

// ===== VALIDATION HELPERS =====
function setError(id, msg) {
  const el = document.getElementById(id);
  const input =
    el.previousElementSibling?.querySelector?.("input") ||
    el.previousElementSibling;
  el.textContent = msg;
  if (input) {
    input.classList.remove("valid");
    input.classList.add("invalid");
  }
}

function setValid(id) {
  const el = document.getElementById(id);
  const input =
    el.previousElementSibling?.querySelector?.("input") ||
    el.previousElementSibling;
  el.textContent = "";
  if (input) {
    input.classList.remove("invalid");
    input.classList.add("valid");
  }
}

function clearForm(form) {
  form.reset();
  form.querySelectorAll(".error-msg").forEach((e) => (e.textContent = ""));
  form
    .querySelectorAll("input")
    .forEach((i) => i.classList.remove("invalid", "valid"));
}

function showToast(message) {
  toast.textContent = message;
  toast.classList.add("show");
  setTimeout(() => toast.classList.remove("show"), 3000);
}

// ===== REAL-TIME VALIDATION =====
function attachLiveValidation(inputId, errId, validator) {
  const input = document.getElementById(inputId);
  input.addEventListener("input", () => {
    const msg = validator(input.value.trim());
    msg ? setError(errId, msg) : setValid(errId);
  });
}

// Login fields
attachLiveValidation("login-username", "login-username-err", (v) =>
  v === "" ? "Username is required." : null
);
attachLiveValidation("login-password", "login-password-err", (v) =>
  v === "" ? "Password is required." : null
);

// Register fields
attachLiveValidation("reg-username", "reg-username-err", (v) => {
  if (v === "") return "Username is required.";
  if (v.length < 3) return "Min 3 characters.";
  if (!/^[a-zA-Z0-9_]+$/.test(v)) return "Only letters, numbers, underscores.";
  return null;
});

attachLiveValidation("reg-email", "reg-email-err", (v) => {
  if (v === "") return "Email is required.";
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) return "Enter a valid email.";
  return null;
});

attachLiveValidation("reg-password", "reg-password-err", (v) => {
  if (v === "") return "Password is required.";
  if (v.length < 6) return "Min 6 characters.";
  return null;
});

attachLiveValidation("reg-confirm", "reg-confirm-err", (v) => {
  if (v === "") return "Please confirm your password.";
  if (v !== document.getElementById("reg-password").value)
    return "Passwords do not match.";
  return null;
});

// ===== LOGIN SUBMIT =====
loginForm.addEventListener("submit", (e) => {
  e.preventDefault();
  let isValid = true;

  const username = document.getElementById("login-username").value.trim();
  const password = document.getElementById("login-password").value.trim();

  if (username === "") {
    setError("login-username-err", "Username is required.");
    isValid = false;
  } else {
    setValid("login-username-err");
  }

  if (password === "") {
    setError("login-password-err", "Password is required.");
    isValid = false;
  } else {
    setValid("login-password-err");
  }

  if (!isValid) return;

  const btn = loginForm.querySelector(".btn");
  const btnText = btn.querySelector(".btn__text") || btn;
  btnText.textContent = "Boarding…";
  btn.disabled = true;

  setTimeout(() => {
    showToast("✓  Welcome aboard! Login successful.");
    btnText.textContent = "Log In";
    btn.disabled = false;
    clearForm(loginForm);
  }, 1200);
});

// ===== REGISTER SUBMIT =====
registerForm.addEventListener("submit", (e) => {
  e.preventDefault();
  let isValid = true;

  const username = document.getElementById("reg-username").value.trim();
  const email = document.getElementById("reg-email").value.trim();
  const password = document.getElementById("reg-password").value;
  const confirm = document.getElementById("reg-confirm").value;

  if (username === "") {
    setError("reg-username-err", "Username is required.");
    isValid = false;
  } else if (username.length < 3) {
    setError("reg-username-err", "Min 3 characters.");
    isValid = false;
  } else if (!/^[a-zA-Z0-9_]+$/.test(username)) {
    setError("reg-username-err", "Only letters, numbers, underscores.");
    isValid = false;
  } else {
    setValid("reg-username-err");
  }

  if (email === "") {
    setError("reg-email-err", "Email is required.");
    isValid = false;
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    setError("reg-email-err", "Enter a valid email.");
    isValid = false;
  } else {
    setValid("reg-email-err");
  }

  if (password === "") {
    setError("reg-password-err", "Password is required.");
    isValid = false;
  } else if (password.length < 6) {
    setError("reg-password-err", "Min 6 characters.");
    isValid = false;
  } else {
    setValid("reg-password-err");
  }

  if (confirm === "") {
    setError("reg-confirm-err", "Please confirm your password.");
    isValid = false;
  } else if (confirm !== password) {
    setError("reg-confirm-err", "Passwords do not match.");
    isValid = false;
  } else {
    setValid("reg-confirm-err");
  }

  if (!isValid) return;

  const btn = registerForm.querySelector(".btn");
  const btnText = btn.querySelector(".btn__text") || btn;
  btnText.textContent = "Issuing ticket…";
  btn.disabled = true;

  setTimeout(() => {
    showToast("🎫  Ticket issued! Redirecting to login…");
    btnText.textContent = "Register";
    btn.disabled = false;
    clearForm(registerForm);

    setTimeout(() => {
      registerCard.classList.remove("active");
      loginCard.classList.add("active");
    }, 1500);
  }, 1200);
});
← Back to timeline