Skip to content

Commit 39e45fe

Browse files
jmacotclaude
andcommitted
feat: add sparkles particle effect to password template
- Full-screen canvas with 120 animated particles behind login card - Gradient accent line (indigo/sky) under header badge - Dark background matching hero style - Respects prefers-reduced-motion - Pure JS canvas implementation (no dependencies) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c158f48 commit 39e45fe

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

password_template.html

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,53 @@
7676
align-items: center;
7777
justify-content: center;
7878
padding: 20px;
79+
position: relative;
80+
overflow: hidden;
81+
background: #0f172a;
82+
}
83+
[data-theme="dark"] .login-wrap { background: #0f172a; }
84+
85+
/* ── SPARKLES CANVAS ── */
86+
#sparkles-canvas {
87+
position: absolute;
88+
inset: 0;
89+
width: 100%;
90+
height: 100%;
91+
z-index: 0;
92+
}
93+
94+
/* ── GRADIENT LINE UNDER TITLE ── */
95+
.sparkle-line {
96+
position: relative;
97+
width: 280px;
98+
height: 40px;
99+
margin: 16px auto 0;
100+
}
101+
.sparkle-line::before {
102+
content: '';
103+
position: absolute;
104+
top: 0;
105+
left: 10%;
106+
right: 10%;
107+
height: 2px;
108+
background: linear-gradient(90deg, transparent, #6366f1, transparent);
109+
}
110+
.sparkle-line::after {
111+
content: '';
112+
position: absolute;
113+
top: 0;
114+
left: 25%;
115+
right: 25%;
116+
height: 4px;
117+
background: linear-gradient(90deg, transparent, #38bdf8, transparent);
118+
filter: blur(2px);
79119
}
80120
.login-container {
81121
width: 100%;
82122
max-width: 440px;
83123
animation: fadeInUp 0.6s ease-out;
124+
position: relative;
125+
z-index: 1;
84126
}
85127

86128
.login-header {
@@ -350,6 +392,7 @@
350392
<!-- Login form (hidden until needed) -->
351393
<div id="staticrypt_content" class="hidden">
352394
<div class="login-wrap">
395+
<canvas id="sparkles-canvas"></canvas>
353396
<div class="login-container">
354397
<div class="login-header">
355398
<div class="icon">
@@ -365,6 +408,7 @@ <h1>/*[|template_title|]*/0</h1>
365408
<span class="dot"></span>
366409
jmacot.github.io
367410
</div>
411+
<div class="sparkle-line"></div>
368412
</div>
369413
<div class="login-body">
370414
<form id="staticrypt-form" action="#" method="post">
@@ -488,6 +532,59 @@ <h1>/*[|template_title|]*/0</h1>
488532
}
489533
});
490534

535+
// ─────────────────────────────────────────────
536+
// SPARKLES CANVAS
537+
// ─────────────────────────────────────────────
538+
(function() {
539+
var canvas = document.getElementById('sparkles-canvas');
540+
if (!canvas) return;
541+
var ctx = canvas.getContext('2d');
542+
var particles = [];
543+
var count = 120;
544+
var prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
545+
if (prefersReduced) return;
546+
547+
function resize() {
548+
canvas.width = canvas.parentElement.offsetWidth;
549+
canvas.height = canvas.parentElement.offsetHeight;
550+
}
551+
resize();
552+
window.addEventListener('resize', resize);
553+
554+
for (var i = 0; i < count; i++) {
555+
particles.push({
556+
x: Math.random() * canvas.width,
557+
y: Math.random() * canvas.height,
558+
r: Math.random() * 1.5 + 0.5,
559+
dx: (Math.random() - 0.5) * 0.4,
560+
dy: (Math.random() - 0.5) * 0.4,
561+
opacity: Math.random(),
562+
opacityDir: (Math.random() > 0.5 ? 1 : -1) * (Math.random() * 0.02 + 0.005)
563+
});
564+
}
565+
566+
function draw() {
567+
ctx.clearRect(0, 0, canvas.width, canvas.height);
568+
for (var i = 0; i < particles.length; i++) {
569+
var p = particles[i];
570+
p.x += p.dx;
571+
p.y += p.dy;
572+
p.opacity += p.opacityDir;
573+
if (p.opacity <= 0.1 || p.opacity >= 1) p.opacityDir *= -1;
574+
if (p.x < 0) p.x = canvas.width;
575+
if (p.x > canvas.width) p.x = 0;
576+
if (p.y < 0) p.y = canvas.height;
577+
if (p.y > canvas.height) p.y = 0;
578+
ctx.beginPath();
579+
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
580+
ctx.fillStyle = 'rgba(255,255,255,' + p.opacity.toFixed(2) + ')';
581+
ctx.fill();
582+
}
583+
requestAnimationFrame(draw);
584+
}
585+
draw();
586+
})();
587+
491588
// ─────────────────────────────────────────────
492589
// DARK MODE (auto-detect)
493590
// ─────────────────────────────────────────────

0 commit comments

Comments
 (0)