Components
A futuristic, sci-fi themed login interface. It features a glass-morphism "membrane" container with smooth mouse-driven parallax depth, ambient bioluminescent background animations, and a magnetic interactive submit button.
Loading preview...
import React, { useRef, MouseEvent } from 'react';
const BioluminescentLogin: React.FC = () => {
const membraneRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
// --- Animation Logic ---
// Parallax effect for the container
const handlePageMouseMove = (e: MouseEvent<HTMLDivElement>) => {
if (!membraneRef.current) return;
// Calculate rotation based on cursor position relative to center
// We divide by 45 to dampen the effect (higher number = less movement)
const xAxis = (window.innerWidth / 2 - e.clientX) / 45;
const yAxis = (window.innerHeight / 2 - e.clientY) / 45;
membraneRef.current.style.transform = `rotateY(${xAxis}deg) rotateX(${yAxis}deg)`;
};
// Magnetic effect for the button
const handleButtonMouseMove = (e: MouseEvent<HTMLButtonElement>) => {
const btn = e.currentTarget;
const rect = btn.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Move button slightly towards cursor
btn.style.transform = `translate(${(x - rect.width / 2) / 5}px, ${(y - rect.height / 2) / 5}px)`;
};
const handleButtonMouseLeave = () => {
if (buttonRef.current) {
buttonRef.current.style.transform = 'translate(0,0)';
}
};
// --- Styles ---
// We use a template literal for the CSS to keep it in one file.
const css = `
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;700&family=Space+Mono&display=swap');
:root {
--bg-deep: #020408;
--membrane-glow: rgba(0, 255, 195, 0.15);
--biolume-core: #00ffc3;
--biolume-secondary: #0084ff;
--tension-line: rgba(255, 255, 255, 0.08);
--font-main: 'Outfit', sans-serif;
--font-mono: 'Space Mono', monospace;
}
/* Reset & Body Context */
.bio-login-wrapper {
background-color: var(--bg-deep);
background-image:
radial-gradient(circle at 50% 50%, rgba(0, 132, 255, 0.05) 0%, transparent 70%),
radial-gradient(circle at 80% 20%, rgba(0, 255, 195, 0.03) 0%, transparent 50%);
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-main);
color: #fff;
overflow: hidden;
position: relative;
}
/* The Membrane */
.membrane-container {
position: relative;
width: 420px;
padding: 60px;
background: rgba(255, 255, 255, 0.01);
backdrop-filter: blur(40px);
border: 1px solid var(--tension-line);
border-radius: 60% 40% 70% 30% / 40% 50% 50% 60%;
box-shadow:
0 0 80px rgba(0, 0, 0, 0.5),
inset 0 0 30px var(--membrane-glow);
animation: pulseMembrane 12s ease-in-out infinite alternate;
z-index: 10;
transition: transform 0.1s ease-out;
}
@keyframes pulseMembrane {
0% { border-radius: 60% 40% 70% 30% / 40% 50% 50% 60%; transform: scale(1); }
100% { border-radius: 40% 60% 30% 70% / 60% 40% 60% 40%; transform: scale(1.02); }
}
/* Bioluminescent Bloom */
.bloom {
position: absolute;
border-radius: 50%;
filter: blur(100px);
z-index: 1;
opacity: 0.4;
pointer-events: none;
}
.bloom-1 { width: 400px; height: 400px; background: var(--biolume-core); top: -100px; left: -100px; animation: float 15s infinite linear; }
.bloom-2 { width: 300px; height: 300px; background: var(--biolume-secondary); bottom: -50px; right: -50px; animation: float 20s infinite linear reverse; }
@keyframes float {
0% { transform: translate(0, 0) rotate(0deg); }
50% { transform: translate(30px, 60px) rotate(180deg); }
100% { transform: translate(0, 0) rotate(360deg); }
}
.form-content { position: relative; z-index: 20; }
h1 {
font-weight: 700;
font-size: 2rem;
letter-spacing: -1px;
margin-bottom: 8px;
background: linear-gradient(to right, #fff, var(--biolume-core));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
font-family: var(--font-mono);
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.4);
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 40px;
display: block;
}
.input-group { position: relative; margin-bottom: 30px; }
.input-group label {
position: absolute;
left: 0;
top: -20px;
font-family: var(--font-mono);
font-size: 0.65rem;
color: var(--biolume-core);
text-transform: uppercase;
opacity: 0.6;
letter-spacing: 1px;
}
input {
width: 100%;
background: transparent;
border: none;
border-bottom: 1px solid var(--tension-line);
padding: 12px 0;
color: #fff;
font-size: 1rem;
outline: none;
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
}
input:focus {
border-bottom-color: var(--biolume-core);
box-shadow: 0 10px 20px -10px rgba(0, 255, 195, 0.2);
}
.input-glow-bar {
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 1px;
background: var(--biolume-core);
box-shadow: 0 0 15px var(--biolume-core);
transition: all 0.6s ease;
transform: translateX(-50%);
}
input:focus + .input-glow-bar { width: 100%; }
.action-container {
margin-top: 50px;
display: flex;
align-items: center;
justify-content: space-between;
}
button {
position: relative;
padding: 16px 32px;
background: transparent;
border: none;
color: #000;
font-weight: 700;
cursor: pointer;
overflow: hidden;
transition: transform 0.1s ease;
z-index: 1;
}
button::before {
content: '';
position: absolute;
inset: 0;
background: var(--biolume-core);
clip-path: polygon(0% 0%, 100% 0%, 90% 100%, 0% 100%);
z-index: -1;
transition: clip-path 0.4s ease;
}
button:hover::before { clip-path: polygon(0% 0%, 90% 0%, 100% 100%, 0% 100%); }
.forgot-link {
font-family: var(--font-mono);
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.3);
text-decoration: none;
transition: color 0.3s ease;
}
.forgot-link:hover { color: var(--biolume-secondary); }
/* Micro-interactions */
.neural-network {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
opacity: 0.2;
}
.reveal-anim {
animation: slideIn 1.2s cubic-bezier(0.23, 1, 0.32, 1) forwards;
opacity: 0;
transform: translateY(20px);
}
@keyframes slideIn { to { opacity: 1; transform: translateY(0); } }
.stagger-1 { animation-delay: 0.1s; }
.stagger-2 { animation-delay: 0.2s; }
.stagger-3 { animation-delay: 0.3s; }
.stagger-4 { animation-delay: 0.4s; }
`;
return (
<>
<style>{css}</style>
<div className="bio-login-wrapper" onMouseMove={handlePageMouseMove}>
<div className="bloom bloom-1"></div>
<div className="bloom bloom-2"></div>
<div className="membrane-container" ref={membraneRef}>
<svg
className="neural-network"
viewBox="0 0 100 100"
preserveAspectRatio="none"
>
<path
d="M0,50 Q25,45 50,50 T100,50"
stroke="rgba(0, 255, 195, 0.2)"
strokeWidth="0.1"
fill="none"
/>
<path
d="M0,30 Q30,35 60,30 T100,30"
stroke="rgba(0, 132, 255, 0.1)"
strokeWidth="0.1"
fill="none"
/>
</svg>
<div className="form-content">
<div className="reveal-anim stagger-1">
<h1>Interface</h1>
<span className="subtitle">Access Protocol 0x-99</span>
</div>
<form onSubmit={(e) => e.preventDefault()}>
<div className="input-group reveal-anim stagger-2">
<label>Identity Node</label>
<input
type="text"
placeholder="user.neural_link"
spellCheck={false}
/>
<div className="input-glow-bar"></div>
</div>
<div className="input-group reveal-anim stagger-3">
<label>Synaptic Key</label>
<input
type="password"
placeholder="••••••••••••"
spellCheck={false}
/>
<div className="input-glow-bar"></div>
</div>
<div className="action-container reveal-anim stagger-4">
<a href="#" className="forgot-link">Sync lost?</a>
<button
type="submit"
ref={buttonRef}
onMouseMove={handleButtonMouseMove}
onMouseLeave={handleButtonMouseLeave}
>
INITIALIZE
</button>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default BioluminescentLogin;