Skip to content

Add Three.js hero animation and refine gradient styling - #2

Merged
MatejMa2ur merged 11 commits into
mainfrom
claude/threejs-animations-blog-design-xl2kxr
Sep 3, 2026
Merged

Add Three.js hero animation and refine gradient styling#2
MatejMa2ur merged 11 commits into
mainfrom
claude/threejs-animations-blog-design-xl2kxr

Conversation

@MatejMa2ur

Copy link
Copy Markdown
Member

Summary

This PR adds an interactive 3D hero animation using Three.js to replace the static Y-A-S logo squares on the homepage, while also refining gradient colors throughout the site for better visual consistency.

Key Changes

  • Hero Animation: Added a Three.js-powered 3D animation that:

    • Renders animated squares that spin and fade in on page load
    • Smoothly transitions to follow the static image position as users scroll
    • Includes scroll-driven rotation wobble and fade-out effects
    • Respects prefers-reduced-motion for accessibility
    • Falls back gracefully to the static image if animation fails
    • Uses brand colors from the Y-A-S mark (green, blue, orange, dark gray)
  • Styling Updates:

    • Updated gradient in .heading-gradient to use CSS variable --danger-color instead of hardcoded #ff6b6b
    • Refined .series-intro background gradient to use a subtle green tint with white
    • Updated .series-stats and blockquote gradients to use --main-light-color variable for consistency
  • Dependencies: Added three@^0.185.1 to support the 3D animation

  • Script Loading: Updated external scripts in BaseLayout to use is:inline attribute for proper Astro handling

Implementation Details

The animation uses an orthographic camera to render 5 layered square meshes with staggered positions. It features a two-phase animation: an initial 500ms spin phase followed by a 1400ms easing transition that moves squares to their final position while fading out the background. Once settled, the animation tracks the fallback image's position in the viewport, enabling scroll-synchronized effects.

https://claude.ai/code/session_011eJ9XiGRuA6XnpgdwYtaiw

claude added 2 commits August 28, 2026 21:41
The hero's static Squares.png is replaced by an animated Three.js
recreation of the brand mark: the diamonds tumble at screen center on
load, the backdrop fades to reveal the page as they settle into the
image's original spot, then they track scroll with a subtle rotation
wobble and fade out once off-screen. Falls back to the static image
when JS/WebGL is unavailable or prefers-reduced-motion is set.

Also swaps the remaining Bootstrap-grey gradients in the blog styles
(post headings, blockquotes, series sidebar, drop-cap) for the site's
own palette tokens.

Marks the layout's external Bootstrap/GitHub-card scripts as is:inline
so Astro's script bundler doesn't merge them into the same module chunk
as the new hero script.
Replace the tumbling-diamond intro with what was actually asked for:
all five pieces start stacked as one plain, rounded white square at
screen center, then explode outward into the brand mark's stacked
diamonds (fading from neutral to each piece's real color as they
separate) and settle at the original bottom-left spot, at a constant
size throughout instead of scaling up mid-animation.

Also switches the flat-cornered planes for a proper rounded-square
shape geometry, matching the corner radii in the source Squares.svg
instead of sharp rectangles.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploying y-a-s with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5c8355f
Status: ✅  Deploy successful!
Preview URL: https://2d67a769.y-a-s.pages.dev
Branch Preview URL: https://claude-threejs-animations-bl.y-a-s.pages.dev

View logs

claude added 9 commits August 29, 2026 15:07
On mobile the hero square is sized by viewport width (.white-square:
100vw x 100vw), but the hero container itself (#home) was still forced
to 100vh via Bootstrap's vh-100 utility. Since 100vw is much shorter
than 100vh on a typical phone, this left several hundred pixels of
bare dark background between the hero content and the page panel below
it. Cap #home to the same 100vw on mobile so the hero's height actually
matches its content.
Extends the existing hero canvas with four small, low-opacity brand
squares anchored near the screen corners. They fade in once the main
logo settles, then each drifts upward at its own parallax speed as the
page scrolls, fading out based on how far it has actually travelled
(so faster squares don't get cut off before slower ones arrive).

Carries the squares motif beyond the hero as a subtle, persistent
visual identity element rather than a one-off intro animation.
The project cards used Bootstrap's default rounded/shadowed .card look
with off-brand purple/blue gradients and stock blue/gray buttons — the
one part of the site that still looked like an unstyled template next
to the custom hero and blog styling. Bring them in line with the
sharp-corner, left-accent-border-on-hover treatment already used by
post-preview cards and the project detail sidebar, and swap the image
overlay gradient and buttons for the site's own palette.

Also drop the unused `github-card` custom-element script from the
layout (dead code — no page ever uses that element, just an extra
external script on every load), and trim the footer's team list down
to a single entry.
The diamond stack was anchored to the raw column edge, bleeding flush
against the left edge of the screen instead of sitting inset with the
rest of the hero content (the Y-A-S heading, nav pill). Anchor it to
the bordered frame's edge plus the diamond's own half-width instead, so
it lines up like everything else in the hero.

Drop the ambient corner squares added earlier — they read as small,
distracting light artifacts rather than a clean accent.

Replace the old "track the DOM slot and fade out after a bit of
scroll" behavior with a sticky one: once the logo settles it stays
pinned on screen, and adds three new sample content sections (How We
Work, Talks & Presentations, Why Y-A-S) plus tags the existing Blog CTA
section, each wired to a scroll-driven "removal" progress computed from
its own position in the viewport. Crossing into a new section peels one
square off the top of the stack (spin, shrink, fade, drift up) using
the same ease() curve as the intro explosion; scrolling back up brings
it back since the effect is a continuous function of scroll position,
not a one-shot trigger. The last (neutral) square has no section of its
own and fades out only once you're near the very bottom of the page, so
nothing lingers stuck over the footer.
… off

The stack was rendering navy-on-top, neutral-on-bottom — backwards from
both the source Squares.svg and the reference the user sent (neutral
top, green, blue, red, navy bottom). Flip the row-to-position mapping
to match.

Replace the "peel off top-down and fly away" scroll effect with a
simpler one: each square now maps directly to one of the 5 content
sections in order (intro, How We Work, Talks & Presentations, Why
Y-A-S, Blog CTA), and shrinks in place — no drift, no extra spin — as
its matching section scrolls into view. Tint each section's background
with that same square's color at low opacity plus a matching left
accent border, so scrolling down visually echoes the logo's own color
order. Force every square down to its smallest size once the footer
itself scrolls into view, since the last section's own threshold isn't
always reachable on a short page and would otherwise leave a full-size
square overlapping the footer branding.

Left the settle size alone per request (matches what's currently
deployed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011eJ9XiGRuA6XnpgdwYtaiw
…e; drop border seams

Turn each of the 5 content sections into a full-width, full-height
screen instead of a short block inside the centered content column,
using the standard position:relative/left:50%/negative-margin bleed
trick so the color wash spans edge to edge while the actual text stays
readable at the original max-width. Guard .yas-page-canvas with
overflow-x:hidden since 100vw can include scrollbar width on some
browsers.

Shrink the logo squares (scale multiplier 1.05 -> 0.7) and anchor them
to the bordered hero frame on the bottom edge too, not just the left,
so the stack sits fully inside the border lines on every side instead
of just horizontally.

Each square was rendered as a lighter outer "frame" plane behind a
smaller colored one, so wherever two overlapping diamonds met in the
stack, a light seam showed at the boundary between colors. Replace the
two-mesh frame+fill pair with a single flat-colored plane per row —
there's no separate border layer left to interrupt, so adjacent colors
now overlap cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011eJ9XiGRuA6XnpgdwYtaiw
Each settled square now raycasts against pointer events (the canvas
itself stays pointer-events:none) so hovering shows a tooltip naming
its section and clicking scrolls smoothly to it. The side-to-side
scroll wobble is removed now that clicking is the primary interaction.

Also restores the neutral outer-frame layer from the brand SVG using
opaque (non-transparent) materials instead of the previous single-mesh
version, so real depth occlusion hides the frame wherever squares
overlap and only each diamond's exposed edge shows a border — matching
the original artwork instead of drawing a full ring per square.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011eJ9XiGRuA6XnpgdwYtaiw
The stacked squares were spaced too tightly, burying most of each
diamond under the next one. Squares.svg spaces rows so each diamond's
tip lands just past the midline of the one above it, ending visibly
inside it rather than disappearing almost entirely — the corrected
0.6421 step ratio (measured from the SVG) restores that look, with
diamond size now also capped by the column's available height so the
taller stack still fits inside the frame.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011eJ9XiGRuA6XnpgdwYtaiw
Suppress the neutral outer-frame mesh's color output (colorWrite:
false) instead of removing it: it stays in the scene for correct
depth occlusion between rows and remains a valid click/hover target,
it just no longer paints a visible border, so the diamonds interlock
with clean edges and no ring at the seams.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011eJ9XiGRuA6XnpgdwYtaiw
@MatejMa2ur
MatejMa2ur merged commit 403625f into main Sep 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants