I Rebuilt My AI News Site to Feel Like an Awwwards Winner (3D, Glass, and a Chromium Quirk) My site had 94 members, 15 people online, and the visual energy of a tax form. So I spent a weekend turning apexnexus.site into the kind of page that wins Awwwards: a Three.js 3D scene in the hero, glassmorphism cards, film grain, a custom cursor, Lenis smooth scroll, an aurora gradient that spins behind the headline. This is the story of what I added, what broke, and the single weirdest CSS bug I have ever debugged. It cost me $0. It took a lot of Playwright screenshots. ## The 3D scene that took 40 lines The hero now runs a real WebGL scene: a wireframe icosahedron with a glowing core, two orbit rings in cyan and violet, and a shell of 150 particles. It rotates on its own and leans toward your mouse. No Three.js framework wrapper, no OrbitControls, no asset pipeline. Just:
var mesh = new THREE.Mesh( new THREE.IcosahedronGeometry(1.35, 1), new THREE.MeshBasicMaterial({ color: 0x34d399, wireframe: true, transparent: true, opacity: 0.5 })
);
scene.add(mesh);
The mouse reaction is just lerping two floats toward the cursor position every frame. That is the whole trick:
mx += (tx - mx) * 0.05;
mesh.rotation.x += 0.0025 + my * 0.008;
Four rules made it sane on mobile: 1. Cap pixel ratio at 2 (Math.min(devicePixelRatio, 2)) or iPhones melt.
- Cut particle count from 150 to 60 on touch devices.
- Respect
prefers-reduced-motion: render one static frame, stop the loop. - Keep a pure-CSS atom fallback that shows if WebGL is missing. An iPhone renders 60 particles and one canvas. A desktop gets the full scene. Nobody notices the difference except the battery. ## The glass card trick Cards got
backdrop-filter: blur(14px)with a gradient background and a 1px highlight line on the top edge:css.card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.14), transparent); }That one pixel line is doing more work than the blur. It reads as "light hitting the top of glass" and instantly separates the card from the background. ## The custom cursor nobody asked for A 6px emerald dot that snaps to the mouse, plus a 34px ring that lerps behind it. The ring grows to 52px and glows when you hover anything interactive. It is the cheapest "this site is alive" signal there is:css #cursor-ring { transition: width.25s, height.25s, border-color.25s; } body.cursor-hover #cursor-ring { width: 52px; height: 52px; }Hidden entirely onpointer: coarse. Touch users never see it, desktop users never stop noticing it. ## The bug that made me question flexbox I added a marquee ticker under the nav: FREE LEARNING HUB, SECURITY PROTOCOLS, BUILD IN PUBLIC, NO PAYWALLS, DAILY AI. The classic pattern. Track is two copies of the text, animatetranslateX(-50%), seamless loop. On desktop: flawless. On iPhone and Android emulation: the words rendered on top of each other. The second copy of the group was drawn directly over the first. I tried everything.flex-shrink: 0on groups.width: max-contenton the track.inline-flexshrink-wrap. Every fix passed on desktop and failed on mobile. The track kept collapsing to the viewport width no matter what I set. Here is the smoking gun. I set the track width to 9999px inline in DevTools:js track.style.width = '9999px'; track.style.minWidth = '9999px';And got: inline width 9999px, computed width 9999px, bounding rect 9999px. Remove theminWidthline and the computed width snaps back to 390px. The element was ignoring a plainwidthon a flex container, in Chromium, only when the content was wider than the viewport. The fix is a two-line JS size function that measures the real rendered group width and sets bothwidthandminWidthto 2x, re-running on font load, window load, and resize:js function size() { var w = group.getBoundingClientRect().width * 2; track.style.width = w + 'px'; track.style.minWidth = w + 'px'; // the only thing Chromium honors }I still do not fully understand whywidthalone was ignored. The lesson is simpler: when a marquee misbehaves, do not trustmax-content. Measure the rendered box and force both properties. ## The iOS overflow that broke everything Same release, different trap: the background parallax layer usedposition: fixed; inset: -30pxso the parallax translate would never show edges. On iPhone that inflated the document width by 100+ pixels, producing a horizontal scroll on every page. The fix wasinset: 0plus ascale(1.06)inside the parallax transform. Same visual result, zero layout overflow. Verify withscrollWidth === clientWidthon an iPhone emulation before you ship anything with fixed elements. ## What actually moved the needle The redesign is nice. The numbers are the point: 94 members, 15 online, all still free, still $0/month to run, rebuilt with no paid assets and no paid libraries. If you are building a content site in 2026, skip the template. Do these five things and you are 80% of the way to feeling expensive: 1. One WebGL scene in the hero, mouse-reactive, mobile-capped. - Glass cards with a top highlight pixel.
- Film grain overlay at 5% opacity.
- Custom cursor for fine pointers only.
- A marquee that you measured, not one you guessed. And if your marquee overlaps on mobile: measure the group, set width and minWidth, re-run on
document.fonts.ready. Trust me on that one. --- 🌐 Live: apexnexus.site - the free AI Nexus learning hub (guides, cheat sheets, daily AI news) 💬 Free AI automation community: Discord
Top comments (0)