Rotating Sunburst
Retro sunburst rays fan out from behind your content and turn slowly, like a vintage poster or a game-show backdrop, with a warm glow in the middle and darker edges. Start them from the centre or rising from the bottom edge.
Grand opening
How to use it
- Paste the snippet just before
</head>. - Add
data-rotating-sunburstto a section.
Options
Add any of these attributes to the same element.
data-colors | The two ray colours. Default #f59e0b,#fbbf24. |
data-rays | How many pairs of rays. Default 14. |
data-duration | Seconds per turn. Default 60. |
data-origin | center or bottom: where the rays start. Default center. |
data-reverse | Turn anticlockwise. |
data-vignette="false" | No glow in the middle or darker edges. |
The rays are a single CSS repeating-conic-gradient turned with a transform, so it costs next to nothing; it pauses when off screen. With reduced motion it holds still.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Rotating Sunburst: retro sunburst rays fan out from behind the content
// and turn slowly, like a vintage poster or a game-show backdrop, softened
// towards the edges. Drawn with a CSS gradient, turned on the GPU, and
// paused when off screen.
// Usage: <section data-rotating-sunburst>…</section>
// Optional: data-colors (the two ray colours, default "#f59e0b,#fbbf24"),
// data-rays (how many pairs of rays, default 14), data-duration (seconds
// per turn, default 60), data-origin ("center" or "bottom", where the rays
// start, default center), data-reverse (turn anticlockwise),
// data-vignette="false" (no darker edges).
var DEFAULTS = { colors: '#f59e0b,#fbbf24', rays: 14, duration: 60 };
var css =
'[data-rotating-sunburst]{position:relative;isolation:isolate;overflow:hidden}' +
'.rsu-layer{position:absolute;inset:0;z-index:-1;pointer-events:none;overflow:hidden}' +
'.rsu-rays{position:absolute;border-radius:50%;will-change:transform}' +
'.rsu-shade{position:absolute;inset:0}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function build(el) {
var d = el.dataset;
var colors = (d.colors || DEFAULTS.colors).split(',').map(function (c) { return c.trim(); });
var c2 = colors[1] || colors[0];
var rays = Math.max(3, +d.rays || DEFAULTS.rays);
var turn = (+d.duration || DEFAULTS.duration) * 1000;
var bottom = d.origin === 'bottom';
var reverse = d.reverse !== undefined && d.reverse !== 'false';
var layer = document.createElement('div');
layer.className = 'rsu-layer';
layer.setAttribute('aria-hidden', 'true');
layer.innerHTML = '<div class="rsu-rays"></div>' + (d.vignette === 'false' ? '' : '<div class="rsu-shade"></div>');
el.insertBefore(layer, el.firstChild);
var disc = layer.firstChild;
// One ray pair repeated round the circle, with a hair of blending on
// each edge so the rays stay crisp but never jagged.
var a = 360 / rays / 2, e = Math.min(0.35, a / 10);
disc.style.background = 'repeating-conic-gradient(from -' + (a / 2) + 'deg,' +
colors[0] + ' 0deg ' + (a - e) + 'deg,' + c2 + ' ' + (a + e) + 'deg ' + (2 * a - e) + 'deg,' + colors[0] + ' ' + (2 * a) + 'deg)';
if (layer.children[1]) {
layer.children[1].style.background = 'radial-gradient(' + (bottom ? 'ellipse 90% 120% at 50% 100%' : 'ellipse at 50% 50%') +
',rgba(255,255,255,.28),transparent 35%,transparent 55%,rgba(60,20,0,.32) 100%)';
}
function resize() {
var w = el.clientWidth, h = el.clientHeight;
var cy = bottom ? h : h / 2;
// Big enough to cover the section at every angle.
var r = Math.sqrt((w / 2) * (w / 2) + Math.max(cy, h - cy) * Math.max(cy, h - cy)) + 4;
disc.style.width = disc.style.height = 2 * r + 'px';
disc.style.left = w / 2 - r + 'px';
disc.style.top = cy - r + 'px';
}
resize();
if (window.ResizeObserver) new ResizeObserver(resize).observe(el);
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var spin = anime.animate(disc, { rotate: reverse ? [0, -360] : [0, 360], duration: turn, ease: 'linear', loop: true, autoplay: false });
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) spin.play(); else spin.pause();
}).observe(el);
}
function init() {
document.querySelectorAll('[data-rotating-sunburst]').forEach(function (el) {
if (el._rsu) return;
el._rsu = true;
build(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><section data-rotating-sunburst style="color: #451a03">
<h1>Grand opening</h1>
</section>
<!-- rising from the bottom, in your own colours: -->
<section data-rotating-sunburst data-origin="bottom" data-colors="#6a0dad,#7e22ce">…</section>