summaryrefslogtreecommitdiff
path: root/internal/generator/theme_neon.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-10 10:23:20 +0300
committerPaul Buetow <paul@buetow.org>2026-04-10 10:23:20 +0300
commitf40fee44e8f256328ca1419863b5441123a1014e (patch)
treeb3a5eabc0b8ac0801240544392edaadf5a6d8ac4 /internal/generator/theme_neon.go
parentbc45b7af3bc93ccd3e4359d29e93417d0af407e1 (diff)
Release v0.1.0v0.1.0
Splash: skip via ?splash=0 on pagination to index; frosted panel and vignette for readable copy; brighter hint/tag colors. Pagination links only at bottom of each page. Tests updated for prev href to index. Made-with: Cursor
Diffstat (limited to 'internal/generator/theme_neon.go')
-rw-r--r--internal/generator/theme_neon.go67
1 files changed, 61 insertions, 6 deletions
diff --git a/internal/generator/theme_neon.go b/internal/generator/theme_neon.go
index 3197d6f..eccf558 100644
--- a/internal/generator/theme_neon.go
+++ b/internal/generator/theme_neon.go
@@ -27,7 +27,8 @@ const neonTemplate = `<!DOCTYPE html>
.logo-title .subtitle { font-size:0.68rem; opacity:0.6; letter-spacing:1px; margin-top:2px; }
.logo-title .subtitle a { color:var(--neon-cyan); text-decoration:none; }
.logo-title .subtitle a:hover { text-shadow:0 0 8px var(--neon-cyan); }
- .nav { display:flex; gap:16px; align-items:center; }
+ .nav { gap:16px; }
+ a.header-feed-link { color:var(--neon-cyan); text-shadow:0 0 8px rgba(0,245,255,0.35); }
.transmit-btn { background:transparent; border:3px solid var(--neon-yellow); color:var(--neon-yellow);
padding:12px 28px; border-radius:9999px; font-weight:600; letter-spacing:1px;
display:flex; align-items:center; gap:10px; box-shadow:0 0 30px var(--neon-yellow);
@@ -80,9 +81,61 @@ const neonTemplate = `<!DOCTYPE html>
header { padding:14px 20px; } .transmit-btn { padding:9px 16px; font-size:0.8rem; }
.nav-hints { display:none; } .modal-inner { padding:24px 16px; }
}
+ .splash-overlay.splash-neon {
+ background: radial-gradient(ellipse 120% 80% at 50% 35%, rgba(0,245,255,0.14) 0%, transparent 55%),
+ radial-gradient(ellipse 90% 55% at 75% 85%, rgba(255,0,204,0.12) 0%, transparent 50%),
+ #0b001a;
+ }
+ .splash-neon .splash-deco {
+ width:100px; height:100px; margin:0 auto 1.25rem; border-radius:50%;
+ border:3px solid var(--neon-cyan); box-shadow:0 0 36px var(--neon-cyan), inset 0 0 26px rgba(0,245,255,0.15);
+ animation: splashNeonSpin 5s linear infinite;
+ }
+ @keyframes splashNeonSpin { to { transform: rotate(360deg); } }
+ .splash-neon .splash-title {
+ font-size: clamp(1.5rem, 5vw, 2.35rem);
+ animation: splashNeonPulse 2s ease-in-out infinite alternate;
+ }
+ @keyframes splashNeonPulse {
+ from { text-shadow: 0 0 12px var(--neon-cyan), 0 0 24px rgba(255,0,204,0.4); }
+ to { text-shadow: 0 0 26px var(--neon-cyan), 0 0 48px var(--neon-magenta); }
+ }
+ .splash-neon .splash-tag { color: var(--neon-yellow); }
+ .splash-neon .splash-hint { color: rgba(224,248,255,0.9); font-family: 'Orbitron', sans-serif; }
+ .splash-neon .splash-inner { text-shadow: 0 2px 24px rgba(0,0,0,0.85), 0 0 40px rgba(11,0,26,0.9); }
</style>
</head>
<body>
+ {{template "splashGate"}}
+ <div id="splash-overlay" class="splash-overlay splash-neon" tabindex="-1" aria-label="Open microblog">
+ <canvas class="splash-gl-canvas" id="splash-gl-canvas" aria-hidden="true"></canvas>
+ <div class="splash-inner">
+ <div class="splash-deco" aria-hidden="true"></div>
+ <div class="splash-title">snonux.foo</div>
+ <div class="splash-tag">Neon Nexus</div>
+ <div class="splash-hint">Click or Enter &mdash; establish link</div>
+ </div>
+ </div>
+ <script>
+ (function(){
+ if(document.documentElement.classList.contains('sno-splash-skip'))return;
+ var cv=document.getElementById('splash-gl-canvas');
+ if(!cv||typeof THREE==='undefined')return;
+ var raf,ren,sc,ca,g=new THREE.Group(),t0=performance.now();
+ function cleanup(){window.removeEventListener('resize',sz);if(raf)cancelAnimationFrame(raf);raf=null;if(ren){ren.dispose();}ren=null;window._snonuxSplashWebGLCleanup=null;}
+ window._snonuxSplashWebGLCleanup=cleanup;
+ function sz(){var w=cv.clientWidth||2,h=cv.clientHeight||2;if(ren)ren.setSize(w,h,false);if(ca){ca.aspect=w/h;ca.updateProjectionMatrix();}}
+ ren=new THREE.WebGLRenderer({canvas:cv,antialias:true,alpha:true});
+ ren.setClearColor(0,0);ren.setPixelRatio(Math.min(window.devicePixelRatio||1,2));
+ sc=new THREE.Scene();ca=new THREE.PerspectiveCamera(52,1,0.1,100);ca.position.set(0,0.4,9);
+ var cols=[0x00f5ff,0xff00cc,0xffe700],i,m;
+ for(i=0;i<3;i++){m=new THREE.Mesh(new THREE.TorusGeometry(1.55+i*0.48,0.055,8,48),new THREE.MeshBasicMaterial({color:cols[i],transparent:true,opacity:0.92}));m.rotation.x=Math.PI/2;m.userData.sp=0.01+i*0.004;g.add(m);}
+ g.add(new THREE.Mesh(new THREE.SphereGeometry(0.52,20,20),new THREE.MeshBasicMaterial({color:0xffe700,transparent:true,opacity:0.95})));
+ sc.add(g);sz();window.addEventListener('resize',sz);
+ function loop(now){raf=requestAnimationFrame(loop);var t=(now-t0)*0.001;g.rotation.y=t*0.42;g.rotation.x=Math.sin(t*0.65)*0.12;g.children.forEach(function(c){if(c.userData.sp)c.rotation.z+=c.userData.sp;});ren.render(sc,ca);}
+ raf=requestAnimationFrame(loop);
+ })();
+ </script>
<canvas id="three-canvas"></canvas>
<div class="overlay">
<header>
@@ -123,9 +176,11 @@ const neonTemplate = `<!DOCTYPE html>
<div class="logo-title">
<h1>snonux.foo</h1>
<p class="subtitle">microblog &mdash; <a href="https://foo.zone">foo.zone</a> is the real blog</p>
+ <p class="logo-host">Site served by a Raspberry Pi 3</p>
</div>
</div>
<div class="nav">
+ <a href="atom.xml" class="header-feed-link" rel="alternate" title="Atom feed" type="application/atom+xml">Atom feed</a>
<a href="https://foo.zone/about" class="transmit-btn">
<i class="fa-solid fa-feather-pointed"></i> TRANSMIT TO NEXUS
</a>
@@ -133,9 +188,6 @@ const neonTemplate = `<!DOCTYPE html>
</header>
{{template "navhints" .}}
<div class="content" id="post-content">
- {{if .PrevPage}}
- <div class="page-nav"><a href="{{.PrevPage}}">&larr; NEWER TRANSMISSIONS</a></div>
- {{end}}
{{range $i, $post := .Posts}}
<div class="post" data-index="{{$i}}" onclick="selectPost({{$i}})">
<div class="post-header">
@@ -145,8 +197,11 @@ const neonTemplate = `<!DOCTYPE html>
<div class="post-text">{{$post.ContentHTML}}</div>
</div>
{{end}}
- {{if .NextPage}}
- <div class="page-nav"><a href="{{.NextPage}}">OLDER TRANSMISSIONS &rarr;</a></div>
+ {{if or .PrevPage .NextPage}}
+ <div class="page-nav page-nav-dual">
+ {{if .PrevPage}}<a href="{{.PrevPage}}">&larr; NEWER TRANSMISSIONS</a>{{end}}
+ {{if .NextPage}}<a href="{{.NextPage}}">OLDER TRANSMISSIONS &rarr;</a>{{end}}
+ </div>
{{end}}
</div>
</div>