summaryrefslogtreecommitdiffstats
path: root/themes/danix-xyz-hacker/assets
diff options
context:
space:
mode:
Diffstat (limited to 'themes/danix-xyz-hacker/assets')
-rw-r--r--themes/danix-xyz-hacker/assets/css/main.css43
-rw-r--r--themes/danix-xyz-hacker/assets/css/main.min.css49
-rw-r--r--themes/danix-xyz-hacker/assets/js/matrix-rain.js157
3 files changed, 249 insertions, 0 deletions
diff --git a/themes/danix-xyz-hacker/assets/css/main.css b/themes/danix-xyz-hacker/assets/css/main.css
index 2b8cbde..53b2410 100644
--- a/themes/danix-xyz-hacker/assets/css/main.css
+++ b/themes/danix-xyz-hacker/assets/css/main.css
@@ -286,3 +286,46 @@ html.theme-light .prose-invert blockquote {
transition-duration: 0.01ms !important;
}
}
+
+/* Matrix rain canvas background */
+#matrix-rain {
+ position: fixed;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 1;
+}
+
+/* Dark theme: 13% opacity (inner pages) */
+html.theme-dark #matrix-rain {
+ opacity: 0.13;
+}
+
+/* Light theme: 18% opacity (inner pages) */
+html.theme-light #matrix-rain {
+ opacity: 0.18;
+}
+
+/* Homepage: more prominent background */
+html.theme-dark body[data-page-kind="home"] #matrix-rain {
+ opacity: 0.28;
+}
+
+html.theme-light body[data-page-kind="home"] #matrix-rain {
+ opacity: 0.35;
+}
+
+/* Reduced motion: hide canvas entirely */
+@media (prefers-reduced-motion: reduce) {
+ #matrix-rain {
+ display: none;
+ }
+}
+
+/* Content grid background — blocks rain under text, visible in gutters (single pages only) */
+.grid.md\:grid-cols-3.gap-8.max-w-7xl.content-grid {
+ position: relative;
+ z-index: 10;
+ background-color: var(--bg);
+}
diff --git a/themes/danix-xyz-hacker/assets/css/main.min.css b/themes/danix-xyz-hacker/assets/css/main.min.css
index 74161bc..9eabce5 100644
--- a/themes/danix-xyz-hacker/assets/css/main.min.css
+++ b/themes/danix-xyz-hacker/assets/css/main.min.css
@@ -2062,6 +2062,55 @@ html.theme-light .prose-invert blockquote {
}
}
+/* Matrix rain canvas background */
+
+#matrix-rain {
+ position: fixed;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 1;
+}
+
+/* Dark theme: 13% opacity (inner pages) */
+
+html.theme-dark #matrix-rain {
+ opacity: 0.13;
+}
+
+/* Light theme: 18% opacity (inner pages) */
+
+html.theme-light #matrix-rain {
+ opacity: 0.18;
+}
+
+/* Homepage: more prominent background */
+
+html.theme-dark body[data-page-kind="home"] #matrix-rain {
+ opacity: 0.28;
+}
+
+html.theme-light body[data-page-kind="home"] #matrix-rain {
+ opacity: 0.35;
+}
+
+/* Reduced motion: hide canvas entirely */
+
+@media (prefers-reduced-motion: reduce) {
+ #matrix-rain {
+ display: none;
+ }
+}
+
+/* Content grid background — blocks rain under text, visible in gutters (single pages only) */
+
+.grid.md\:grid-cols-3.gap-8.max-w-7xl.content-grid {
+ position: relative;
+ z-index: 10;
+ background-color: var(--bg);
+}
+
.hover\:bg-surface:hover {
background-color: var(--surface);
}
diff --git a/themes/danix-xyz-hacker/assets/js/matrix-rain.js b/themes/danix-xyz-hacker/assets/js/matrix-rain.js
new file mode 100644
index 0000000..53c55d8
--- /dev/null
+++ b/themes/danix-xyz-hacker/assets/js/matrix-rain.js
@@ -0,0 +1,157 @@
+// Matrix rain background effect
+(function() {
+ // Bail out if user prefers reduced motion
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
+ return;
+ }
+
+ // Canvas and context
+ let canvas = document.getElementById('matrix-rain');
+ if (!canvas) return;
+ const ctx = canvas.getContext('2d');
+
+ // State
+ let columns = [];
+ let frameCount = 0;
+ let colors = { accent: '#a855f7', accent2: '#00ff88', bg: '#060b10', head: '#ffffff' };
+
+ // Character set: 30% ASCII, 70% katakana
+ const ASCII = Array.from({ length: 94 }, (_, i) => String.fromCharCode(33 + i));
+ const KATA = Array.from({ length: 96 }, (_, i) => String.fromCodePoint(0x30a0 + i));
+ const CHARS = shuffle([...ASCII, ...KATA, ...KATA, ...KATA]);
+
+ // Utility: shuffle array
+ function shuffle(arr) {
+ for (let i = arr.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [arr[i], arr[j]] = [arr[j], arr[i]];
+ }
+ return arr;
+ }
+
+ // Utility: convert hex or rgb color to rgba string
+ function hexToRgba(color, alpha) {
+ const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
+ if (rgbMatch) {
+ return `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${alpha})`;
+ }
+ const hex = color.replace('#', '');
+ const r = parseInt(hex.substring(0, 2), 16);
+ const g = parseInt(hex.substring(2, 4), 16);
+ const b = parseInt(hex.substring(4, 6), 16);
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+ }
+
+ // Sample CSS variables based on current theme
+ function sampleColors() {
+ const style = getComputedStyle(document.documentElement);
+ const isDark = document.documentElement.classList.contains('theme-dark');
+ colors.accent = style.getPropertyValue('--accent').trim();
+ colors.accent2 = style.getPropertyValue('--accent2').trim();
+ colors.bg = style.getPropertyValue('--bg').trim();
+ // Head char: bright white in dark mode, deep purple-black in light mode
+ colors.head = isDark ? '#ffffff' : '#1a0533';
+ }
+
+ // Resize canvas to window dimensions
+ function resizeCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ ctx.font = '14px "JetBrains Mono", monospace';
+ ctx.textBaseline = 'top';
+ initColumns();
+ }
+
+ // Initialize columns for the current canvas width
+ function initColumns() {
+ columns = [];
+ const columnWidth = 14;
+ const columnCount = Math.floor(canvas.width / columnWidth);
+
+ for (let i = 0; i < columnCount; i++) {
+ columns.push({
+ x: i * columnWidth,
+ y: -Math.floor(Math.random() * 40), // stagger start above viewport
+ speed: 2 + Math.floor(Math.random() * 3), // 2-4 frames between drops
+ color: Math.random() < 0.6 ? 'accent2' : 'accent', // 60% green, 40% purple
+ charIndex: Math.floor(Math.random() * CHARS.length),
+ length: 8 + Math.floor(Math.random() * 13), // trail length 8-20
+ });
+ }
+ }
+
+ // Set up MutationObserver for theme switching
+ function setupThemeObserver() {
+ const observer = new MutationObserver(function(mutations) {
+ for (const m of mutations) {
+ if (m.attributeName === 'class') {
+ sampleColors();
+ break;
+ }
+ }
+ });
+ observer.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ['class'],
+ });
+ }
+
+ // Main animation loop
+ function drawFrame() {
+ frameCount++;
+
+ // Fade layer: semi-transparent background fill
+ ctx.fillStyle = hexToRgba(colors.bg, 0.085);
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Draw each column
+ for (const col of columns) {
+ // Skip if not time to drop yet (per-column throttle)
+ if (frameCount % col.speed !== 0) continue;
+
+ // Draw explicit trail in column color
+ ctx.fillStyle = colors[col.color];
+ for (let i = 1; i <= col.length; i++) {
+ const trailY = (col.y - i) * 14;
+ if (trailY < 0) continue;
+ const trailCharIndex = (col.charIndex - i + CHARS.length) % CHARS.length;
+ ctx.fillText(CHARS[trailCharIndex], col.x, trailY);
+ }
+
+ // Draw head character (bright)
+ ctx.fillStyle = colors.head;
+ const headCharIndex = col.charIndex % CHARS.length;
+ ctx.fillText(CHARS[headCharIndex], col.x, col.y * 14);
+
+ // Advance column
+ col.y++;
+ col.charIndex = (col.charIndex + 1) % CHARS.length;
+
+ // Reset when scrolled off screen
+ if (col.y * 14 > canvas.height + col.length * 14) {
+ col.y = -Math.floor(Math.random() * 20);
+ col.charIndex = Math.floor(Math.random() * CHARS.length);
+ col.color = Math.random() < 0.6 ? 'accent2' : 'accent';
+ }
+ }
+
+ requestAnimationFrame(drawFrame);
+ }
+
+ // Initialize
+ sampleColors();
+ resizeCanvas();
+ setupThemeObserver();
+
+ // Debounced resize handler
+ let resizeTimer;
+ window.addEventListener('resize', function() {
+ clearTimeout(resizeTimer);
+ resizeTimer = setTimeout(resizeCanvas, 150);
+ });
+
+ // Start animation when fonts are ready
+ document.fonts.ready.then(function() {
+ requestAnimationFrame(drawFrame);
+ });
+})();