Last active
January 4, 2026 17:05
-
-
Save jerlendds/f31d0ae9973b7f5684bdf66d5993eedb to your computer and use it in GitHub Desktop.
Poor clone of https://manus.im/login?redirectUrl=%2Fapp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Pure CSS Dotted Aurora</title> | |
| <style> | |
| :root { | |
| --bg: #05060a; | |
| --dot: rgba(255, 255, 255, 0.11); | |
| --dot-size: 1.5px; | |
| --dot-gap: 16px; | |
| /* blob colors */ | |
| --c1: rgba(0, 140, 255, 0.35); | |
| --c2: rgba(170, 0, 255, 0.28); | |
| --c3: rgba(255, 60, 160, 0.22); | |
| --c4: rgba(0, 255, 200, 0.18); | |
| /* motion tuning */ | |
| --dur: 38s; | |
| } | |
| html, | |
| body { | |
| height: 100%; | |
| } | |
| body { | |
| margin: 0; | |
| background: var(--bg); | |
| color: white; | |
| overflow: hidden; | |
| } | |
| .stage { | |
| position: relative; | |
| height: 100%; | |
| width: 100%; | |
| display: grid; | |
| place-items: center; | |
| isolation: isolate; | |
| } | |
| .bg { | |
| position: absolute; | |
| inset: 0; | |
| z-index: 0; | |
| background: var(--bg); | |
| } | |
| .dotlayer { | |
| position: absolute; | |
| inset: 0; | |
| z-index: 1; | |
| pointer-events: none; | |
| /* neutral dot color ink */ | |
| background: var(--dot); | |
| /* fixed dot mask (grid never moves) */ | |
| -webkit-mask-image: radial-gradient( | |
| circle at center, | |
| #fff var(--dot-size), | |
| transparent calc(var(--dot-size) + 0.1px) | |
| ); | |
| mask-image: radial-gradient( | |
| circle at center, | |
| #fff var(--dot-size), | |
| transparent calc(var(--dot-size) + 0.1px) | |
| ); | |
| -webkit-mask-size: var(--dot-gap) var(--dot-gap); | |
| mask-size: var(--dot-gap) var(--dot-gap); | |
| -webkit-mask-position: 0 0; | |
| mask-position: 0 0; | |
| -webkit-mask-repeat: repeat; | |
| mask-repeat: repeat; | |
| /* shapes tint/brighten the dot ink */ | |
| mix-blend-mode: screen; | |
| isolation: isolate; | |
| } | |
| /* ===== Moving solid-color shapes (they live INSIDE .dotlayer) ===== */ | |
| .shapes { | |
| position: absolute; | |
| inset: 0; | |
| pointer-events: none; | |
| } | |
| .shape { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| opacity: 1; | |
| will-change: transform; | |
| transform-origin: 50% 50%; | |
| /* SOLID decal color (no gradients) | |
| NOTE: some shapes (e.g. .hollow) override background/border. */ | |
| background: var(--fill); | |
| box-shadow: none; | |
| /* JS drives motion via transform */ | |
| transform: translate3d(0, 0, 0) rotate(0deg); | |
| } | |
| /* Individual decals (SOLID fills). Motion is driven by JS (bouncing). */ | |
| .hex { | |
| width: 300px; | |
| height: 300px; | |
| clip-path: polygon(25% 6%, 75% 6%, 97% 50%, 75% 94%, 25% 94%, 3% 50%); | |
| --fill: rgba(0, 140, 255, 0.38); | |
| } | |
| .tri { | |
| width: 320px; | |
| height: 320px; | |
| clip-path: polygon(50% 4%, 96% 92%, 4% 92%); | |
| --fill: rgba(255, 60, 160, 0.25); | |
| } | |
| .sq { | |
| width: 240px; | |
| height: 240px; | |
| border-radius: 12px; | |
| --fill: rgba(0, 255, 200, 0.3); | |
| } | |
| .circ { | |
| width: 360px; | |
| height: 360px; | |
| border-radius: 9999px; | |
| --fill: rgba(255, 255, 255, 0.2); | |
| opacity: 0.55; | |
| } | |
| /* 6-protrusion asterisk decal (LAYERED approach)*/ | |
| .asterisk { | |
| width: 300px; | |
| height: 300px; | |
| --fill: rgba(255, 160, 60, 0.315); | |
| background: transparent; /* bars carry fill */ | |
| opacity: 0.85; | |
| } | |
| .asterisk .bar { | |
| position: absolute; | |
| left: 50%; | |
| top: 50%; | |
| width: 86%; | |
| height: 20%; | |
| background: var(--fill); | |
| border-radius: 6px; | |
| transform: translate(-50%, -50%) rotate(var(--a)); | |
| } | |
| /* rectangular protrusions (caps) */ | |
| .asterisk .bar::before, | |
| .asterisk .bar::after { | |
| content: ""; | |
| position: absolute; | |
| top: 50%; | |
| width: 16%; | |
| height: 140%; | |
| background: inherit; | |
| transform: translateY(-50%); | |
| border-radius: 4px; | |
| } | |
| .asterisk .bar::before { | |
| left: -6%; | |
| } | |
| .asterisk .bar::after { | |
| right: -6%; | |
| } | |
| /* angles (3 bars => 6 ends) */ | |
| .asterisk .b0 { | |
| --a: 0deg; | |
| } | |
| .asterisk .b1 { | |
| --a: 60deg; | |
| } | |
| .asterisk .b2 { | |
| --a: 120deg; | |
| } | |
| /* Hollow ring: border only (extra thick) */ | |
| .hollow { | |
| width: 360px; | |
| height: 360px; | |
| border-radius: 9999px; | |
| background: transparent; | |
| /* extra-thick ring */ | |
| border: 54px solid rgba(80, 180, 255, 0.315); | |
| opacity: 0.75; | |
| } | |
| /* Small inner circle (matches hollow cutout scale) */ | |
| .dotcirc { | |
| width: 260px; | |
| height: 260px; | |
| border-radius: 9999px; | |
| --fill: rgba(0, 183, 255, 0.315); | |
| opacity: 0.65; | |
| } | |
| /* Simple rectangle */ | |
| .rect { | |
| width: 420px; | |
| height: 200px; | |
| border-radius: 14px; | |
| --fill: rgba(120, 90, 255, 0.315); | |
| opacity: 0.7; | |
| } | |
| @media (prefers-reduced-motion: reduce) { | |
| .shape { | |
| animation: none; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <main class="stage"> | |
| <div class="bg" aria-hidden="true"></div> | |
| <div class="dotlayer" aria-hidden="true"> | |
| <div class="shapes" aria-hidden="true"> | |
| <div class="shape hex"></div> | |
| <div class="shape tri"></div> | |
| <div class="shape sq"></div> | |
| <div class="shape circ"></div> | |
| <div class="shape asterisk" aria-hidden="true"> | |
| <span class="bar b0"></span> | |
| <span class="bar b1"></span> | |
| <span class="bar b2"></span> | |
| </div> | |
| <div class="shape hollow"></div> | |
| <div class="shape dotcirc"></div> | |
| <div class="shape rect"></div> | |
| </div> | |
| </div> | |
| </main> | |
| <script> | |
| // Bouncing decals with overshoot. | |
| const OVERSHOOT = 50; // px beyond edge before bounce | |
| const MIN_SPEED = 150; // px/s | |
| const MAX_SPEED = 170; // px/s | |
| const ANGLE_JITTER = 0.15; // radians; how much to change angle on bounce | |
| const stage = document.querySelector(".stage"); | |
| const shapes = Array.from(document.querySelectorAll(".shape")); | |
| function rand(min, max) { | |
| return min + Math.random() * (max - min); | |
| } | |
| function clamp(v, lo, hi) { | |
| return Math.max(lo, Math.min(hi, v)); | |
| } | |
| function makeState(el, i) { | |
| const r = el.getBoundingClientRect(); | |
| const speed = rand(MIN_SPEED, MAX_SPEED); | |
| const angle = rand(0, Math.PI * 2); | |
| // start roughly distributed | |
| const W = stage.clientWidth; | |
| const H = stage.clientHeight; | |
| const x = rand(0, Math.max(1, W - r.width)); | |
| const y = rand(0, Math.max(1, H - r.height)); | |
| return { | |
| el, | |
| w: r.width, | |
| h: r.height, | |
| x, | |
| y, | |
| vx: Math.cos(angle) * speed, | |
| vy: Math.sin(angle) * speed, | |
| speed, | |
| rot: rand(0, Math.PI * 2), | |
| // default slow rotation; some shapes override below | |
| rotSpeed: rand(-0.22, 0.22), | |
| }; | |
| } | |
| let states = shapes.map(makeState); | |
| // Per-shape rotation tuning | |
| // Asterisk: much slower than everything else | |
| for (const st of states) { | |
| if (st.el.classList.contains("asterisk")) { | |
| st.rotSpeed = rand(-0.035, 0.035); | |
| } | |
| } | |
| let last = performance.now(); | |
| function renorm(state) { | |
| const s = Math.hypot(state.vx, state.vy) || 1; | |
| const target = state.speed; | |
| state.vx = (state.vx / s) * target; | |
| state.vy = (state.vy / s) * target; | |
| } | |
| function jitter(state) { | |
| // rotate velocity vector by a small random angle | |
| const a = rand(-ANGLE_JITTER, ANGLE_JITTER); | |
| const ca = Math.cos(a), | |
| sa = Math.sin(a); | |
| const vx = state.vx * ca - state.vy * sa; | |
| const vy = state.vx * sa + state.vy * ca; | |
| state.vx = vx; | |
| state.vy = vy; | |
| renorm(state); | |
| // also nudge rotation speed on bounce (subtle) | |
| // keep asterisk very slow even after bounce | |
| if (state.el.classList.contains("asterisk")) { | |
| state.rotSpeed = clamp( | |
| state.rotSpeed + rand(-0.008, 0.008), | |
| -0.05, | |
| 0.05 | |
| ); | |
| } else { | |
| state.rotSpeed = clamp( | |
| state.rotSpeed + rand(-0.06, 0.06), | |
| -0.35, | |
| 0.35 | |
| ); | |
| } | |
| } | |
| function step(now) { | |
| const dt = clamp((now - last) / 1000, 0, 0.05); | |
| last = now; | |
| const W = stage.clientWidth; | |
| const H = stage.clientHeight; | |
| for (const s of states) { | |
| s.x += s.vx * dt; | |
| s.y += s.vy * dt; | |
| s.rot += s.rotSpeed * dt; | |
| // extended bounds so it travels ~50px past the edge | |
| const minX = -OVERSHOOT; | |
| const maxX = W - s.w + OVERSHOOT; | |
| const minY = -OVERSHOOT; | |
| const maxY = H - s.h + OVERSHOOT; | |
| let bounced = false; | |
| if (s.x < minX) { | |
| s.x = minX; | |
| s.vx = Math.abs(s.vx); | |
| bounced = true; | |
| } else if (s.x > maxX) { | |
| s.x = maxX; | |
| s.vx = -Math.abs(s.vx); | |
| bounced = true; | |
| } | |
| if (s.y < minY) { | |
| s.y = minY; | |
| s.vy = Math.abs(s.vy); | |
| bounced = true; | |
| } else if (s.y > maxY) { | |
| s.y = maxY; | |
| s.vy = -Math.abs(s.vy); | |
| bounced = true; | |
| } | |
| if (bounced) jitter(s); | |
| const deg = (s.rot * 180) / Math.PI; | |
| s.el.style.transform = `translate3d(${s.x}px, ${s.y}px, 0) rotate(${deg}deg)`; | |
| } | |
| requestAnimationFrame(step); | |
| } | |
| // Keep sizes in sync on resize (important for correct bounds) | |
| const ro = new ResizeObserver(() => { | |
| states = states.map((st) => { | |
| const r = st.el.getBoundingClientRect(); | |
| st.w = r.width; | |
| st.h = r.height; | |
| return st; | |
| }); | |
| }); | |
| ro.observe(document.body); | |
| requestAnimationFrame(step); | |
| </script> | |
| </body> | |
| </html> |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
animated-dot-shape-mask.html.webm