SVG Editorial Spotlight
An immersive standalone opener where SVG light, orbit lines and editorial copy form a reactive stage.
This experiment requires full-page rendering.
Open live demoHTML
<div class="svg-editorial-spotlight" data-spotlight-root>
<section class="spotlight-stage">
<div class="spotlight-copy">
<p class="spotlight-eyebrow">Immersive editorial opener</p>
<p class="spotlight-kicker" data-spotlight-current>Phase 01: Signal</p>
<h1>Turn a manifesto into a moving atmosphere</h1>
<p class="spotlight-intro">
This concept explores how an editorial statement can feel less like a static hero block and more like a live
signal. SVG beams, orbit lines and a responsive glow turn the opening of the page into an active scene.
</p>
<a class="spotlight-cta" href="#spotlight-phases">Explore the sequence</a>
</div>
<div class="spotlight-visual" aria-hidden="true">
<svg class="spotlight-svg" viewBox="0 0 1200 900" role="presentation">
<defs>
<linearGradient id="spotlightBeam" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#6ef3ff" />
<stop offset="50%" stop-color="#8aa6ff" />
<stop offset="100%" stop-color="#ffb45f" />
</linearGradient>
<radialGradient id="spotlightGlow" cx="50%" cy="50%" r="60%">
<stop offset="0%" stop-color="rgba(255,255,255,0.95)" />
<stop offset="40%" stop-color="rgba(110,243,255,0.35)" />
<stop offset="100%" stop-color="rgba(110,243,255,0)" />
</radialGradient>
<filter id="spotlightBlur">
<feGaussianBlur stdDeviation="18" />
</filter>
</defs>
<g class="spotlight-halo">
<circle cx="720" cy="380" r="230" fill="url(#spotlightGlow)" filter="url(#spotlightBlur)" />
<circle cx="720" cy="380" r="150" class="spotlight-disc" />
</g>
<g class="spotlight-orbits">
<ellipse class="orbit orbit-a" cx="710" cy="395" rx="335" ry="220" />
<ellipse class="orbit orbit-b" cx="720" cy="390" rx="250" ry="150" />
<ellipse class="orbit orbit-c" cx="705" cy="400" rx="410" ry="290" />
</g>
<g class="spotlight-beams">
<path class="beam beam-a" d="M1180 140C1030 185 960 215 885 280C790 360 755 430 630 560" />
<path class="beam beam-b" d="M1090 760C940 675 865 625 790 560C700 485 660 415 560 265" />
<path class="beam beam-c" d="M300 95C420 180 485 240 560 315C645 400 695 455 835 610" />
</g>
<g class="spotlight-nodes">
<g class="node node-signal is-active" data-spotlight-node="signal">
<circle cx="522" cy="316" r="11" />
<circle cx="522" cy="316" r="28" class="node-ring" />
</g>
<g class="node node-bloom" data-spotlight-node="bloom">
<circle cx="884" cy="296" r="11" />
<circle cx="884" cy="296" r="28" class="node-ring" />
</g>
<g class="node node-release" data-spotlight-node="release">
<circle cx="772" cy="612" r="11" />
<circle cx="772" cy="612" r="28" class="node-ring" />
</g>
</g>
</svg>
</div>
</section>
<section class="spotlight-phases" id="spotlight-phases">
<article class="spotlight-phase is-active" data-spotlight-phase="signal">
<p class="spotlight-phase__eyebrow">Phase 01</p>
<h2>Signal</h2>
<p>
The page opens in a calm but charged state. The beams stay soft, the glow is wide, and the copy reads like the
first line of a manifesto.
</p>
</article>
<article class="spotlight-phase" data-spotlight-phase="bloom">
<p class="spotlight-phase__eyebrow">Phase 02</p>
<h2>Bloom</h2>
<p>
As the reader advances, the SVG structure grows brighter and tighter. The atmosphere feels less static and more
intentionally composed around the editorial claim.
</p>
</article>
<article class="spotlight-phase" data-spotlight-phase="release">
<p class="spotlight-phase__eyebrow">Phase 03</p>
<h2>Release</h2>
<p>
The final phase relaxes into a wider spread of light and motion, giving the scene a sense of completion instead
of ending on a hard stop.
</p>
</article>
</section>
</div>
CSS
.svg-editorial-spotlight {
--pointer-x: 50;
--pointer-y: 50;
--scroll-progress: 0;
--stage-accent: #6ef3ff;
--stage-accent-2: #ffb45f;
max-width: 1220px;
margin: 0 auto;
color: #f5f7fb;
}
.spotlight-stage {
position: relative;
min-height: 86vh;
display: grid;
align-items: end;
padding: 42px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 34px;
overflow: hidden;
background:
radial-gradient(circle at calc(var(--pointer-x) * 1%) calc(var(--pointer-y) * 1%), rgba(110, 243, 255, 0.14), transparent 28%),
radial-gradient(circle at 78% 18%, rgba(255, 180, 95, 0.16), transparent 22%),
linear-gradient(135deg, #070c14 0%, #0d1324 38%, #120d17 100%);
box-shadow: 0 28px 90px rgba(0, 0, 0, 0.34);
}
.spotlight-stage::before,
.spotlight-stage::after {
content: "";
position: absolute;
inset: auto;
pointer-events: none;
border-radius: 999px;
filter: blur(24px);
}
.spotlight-stage::before {
width: 280px;
height: 280px;
top: calc(10% + (var(--pointer-y) * 0.16%));
left: calc(52% + (var(--pointer-x) * 0.08%));
background: rgba(110, 243, 255, 0.16);
animation: spotlightFloat 10s ease-in-out infinite;
}
.spotlight-stage::after {
width: 240px;
height: 240px;
right: 8%;
bottom: 12%;
background: rgba(255, 180, 95, 0.14);
animation: spotlightFloat 13s ease-in-out infinite reverse;
}
.spotlight-copy {
position: relative;
z-index: 2;
max-width: 520px;
}
.spotlight-eyebrow,
.spotlight-phase__eyebrow {
margin: 0 0 12px;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.18em;
color: rgba(255, 255, 255, 0.6);
}
.spotlight-kicker {
margin: 0 0 18px;
color: var(--stage-accent);
font-weight: 700;
letter-spacing: 0.04em;
}
.spotlight-copy h1 {
margin: 0;
max-width: 10ch;
font-size: clamp(3.4rem, 8vw, 6.4rem);
line-height: 0.9;
text-wrap: balance;
}
.spotlight-intro {
margin: 20px 0 0;
max-width: 42ch;
font-size: 1.08rem;
line-height: 1.8;
color: rgba(245, 247, 251, 0.82);
}
.spotlight-cta {
display: inline-flex;
align-items: center;
gap: 10px;
margin-top: 28px;
padding: 12px 18px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.14);
color: #ffffff;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
}
.spotlight-cta:hover {
border-color: rgba(110, 243, 255, 0.4);
}
.spotlight-visual {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
}
.spotlight-svg {
width: 100%;
height: 100%;
}
.spotlight-disc {
fill: rgba(255, 255, 255, 0.08);
transform-origin: 720px 380px;
transform: scale(calc(1 + (var(--scroll-progress) * 0.0007)));
}
.spotlight-orbits,
.spotlight-beams,
.spotlight-nodes {
transform: translate(
calc((var(--pointer-x) - 50) * 0.75px),
calc((var(--pointer-y) - 50) * 0.55px)
);
transform-origin: 720px 380px;
transition: transform 180ms ease-out;
}
.orbit {
fill: none;
stroke: rgba(255, 255, 255, 0.16);
stroke-width: 1.2;
stroke-dasharray: 12 16;
animation: orbitSpin 26s linear infinite;
}
.orbit-b {
stroke: rgba(110, 243, 255, 0.22);
animation-duration: 18s;
animation-direction: reverse;
}
.orbit-c {
stroke: rgba(255, 180, 95, 0.2);
animation-duration: 32s;
}
.beam {
fill: none;
stroke: url(#spotlightBeam);
stroke-width: 2;
stroke-linecap: round;
opacity: 0.52;
stroke-dasharray: 10 18;
animation: beamFlow 6s linear infinite;
}
.beam-b {
animation-duration: 8s;
}
.beam-c {
animation-duration: 7.2s;
}
.node circle:first-child {
fill: #ffffff;
opacity: 0.92;
}
.node-ring {
fill: none;
stroke: rgba(255, 255, 255, 0.32);
stroke-width: 1.6;
animation: nodePulse 3.6s ease-out infinite;
}
.node {
opacity: 0.26;
transition: opacity 220ms ease;
}
.node.is-active {
opacity: 1;
}
.node-signal.is-active circle:first-child {
fill: #6ef3ff;
}
.node-bloom.is-active circle:first-child {
fill: #8aa6ff;
}
.node-release.is-active circle:first-child {
fill: #ffb45f;
}
.svg-editorial-spotlight[data-active-phase="signal"] {
--stage-accent: #6ef3ff;
--stage-accent-2: #8aa6ff;
}
.svg-editorial-spotlight[data-active-phase="bloom"] {
--stage-accent: #8aa6ff;
--stage-accent-2: #ff7cbf;
}
.svg-editorial-spotlight[data-active-phase="release"] {
--stage-accent: #ffb45f;
--stage-accent-2: #ffe36a;
}
.spotlight-phases {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin-top: 18px;
}
.spotlight-phase {
padding: 24px;
min-height: 260px;
border-radius: 24px;
border: 1px solid rgba(255, 255, 255, 0.08);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 60%),
rgba(9, 10, 12, 0.96);
transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease;
}
.spotlight-phase.is-active {
transform: translateY(-4px);
border-color: rgba(110, 243, 255, 0.32);
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.18);
}
.spotlight-phase h2 {
margin: 0 0 12px;
font-size: 2rem;
line-height: 0.96;
}
.spotlight-phase p:last-child {
margin: 0;
color: rgba(245, 247, 251, 0.8);
line-height: 1.75;
}
@keyframes orbitSpin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes beamFlow {
from {
stroke-dashoffset: 0;
}
to {
stroke-dashoffset: -220;
}
}
@keyframes nodePulse {
0% {
transform: scale(0.72);
opacity: 0.7;
}
100% {
transform: scale(1.3);
opacity: 0;
}
}
@keyframes spotlightFloat {
0%,
100% {
transform: translate3d(0, 0, 0);
}
50% {
transform: translate3d(12px, -16px, 0);
}
}
@media only screen and (max-width: 980px) {
.spotlight-stage {
min-height: 74vh;
padding: 28px;
}
.spotlight-copy h1 {
max-width: 12ch;
}
.spotlight-phases {
grid-template-columns: 1fr;
}
}
JavaScript
(function () {
const root = document.querySelector('[data-spotlight-root]');
if (!root) return;
const phases = Array.from(root.querySelectorAll('[data-spotlight-phase]'));
const nodes = Array.from(root.querySelectorAll('[data-spotlight-node]'));
const currentLabel = root.querySelector('[data-spotlight-current]');
if (!phases.length || !nodes.length || !currentLabel) return;
let pointerX = 50;
let pointerY = 50;
let targetX = 50;
let targetY = 50;
function setActivePhase(phase) {
const phaseName = phase.dataset.spotlightPhase;
const phaseTitle = phase.querySelector('h2');
root.dataset.activePhase = phaseName;
phases.forEach(item => {
item.classList.toggle('is-active', item === phase);
});
nodes.forEach(node => {
node.classList.toggle('is-active', node.dataset.spotlightNode === phaseName);
});
if (phaseTitle) {
const phaseIndex = phases.indexOf(phase) + 1;
currentLabel.textContent = `Phase 0${phaseIndex}: ${phaseTitle.textContent}`;
}
}
function updatePointer(event) {
const rect = root.getBoundingClientRect();
targetX = ((event.clientX - rect.left) / rect.width) * 100;
targetY = ((event.clientY - rect.top) / rect.height) * 100;
}
function resetPointer() {
targetX = 50;
targetY = 50;
}
function animatePointer() {
pointerX += (targetX - pointerX) * 0.08;
pointerY += (targetY - pointerY) * 0.08;
root.style.setProperty('--pointer-x', pointerX.toFixed(2));
root.style.setProperty('--pointer-y', pointerY.toFixed(2));
requestAnimationFrame(animatePointer);
}
function updateScrollProgress() {
const rect = root.getBoundingClientRect();
const total = rect.height + window.innerHeight;
const seen = Math.min(Math.max(window.innerHeight - rect.top, 0), total);
const progress = total > 0 ? (seen / total) * 100 : 0;
root.style.setProperty('--scroll-progress', progress.toFixed(2));
}
const observer = new IntersectionObserver(
entries => {
let nextPhase = null;
let highestRatio = 0;
entries.forEach(entry => {
if (entry.intersectionRatio > highestRatio) {
highestRatio = entry.intersectionRatio;
nextPhase = entry.target;
}
});
if (nextPhase) {
setActivePhase(nextPhase);
}
},
{
threshold: [0.25, 0.45, 0.65]
}
);
phases.forEach(phase => observer.observe(phase));
root.addEventListener('pointermove', updatePointer);
root.addEventListener('pointerleave', resetPointer);
window.addEventListener('scroll', updateScrollProgress, { passive: true });
setActivePhase(phases[0]);
updateScrollProgress();
animatePointer();
})();
What it solves
Creates a stronger sense of spectacle and brand presence for flagship editorial moments without needing canvas, video or external dependencies.
SVG Editorial Spotlight is a more theatrical experiment than the previous Nebula patterns. Instead of focusing on utility chrome or reading assistance, it turns the opening of a page into a moving editorial scene built around animated SVG beams, orbit lines and a responsive glow.
The goal is to explore how a manifesto, campaign intro or premium feature opener can feel immersive without relying on heavy libraries or video. Motion, atmosphere and hierarchy all come from CSS, SVG and a small layer of JavaScript.