Skip to content

feat: differentiated hero + GSAP motion - #9

Merged
murilopmachado merged 1 commit into
mainfrom
polish/gsap-and-differentiation
Aug 4, 2026
Merged

feat: differentiated hero + GSAP motion#9
murilopmachado merged 1 commit into
mainfrom
polish/gsap-and-differentiation

Conversation

@murilopmachado

Copy link
Copy Markdown
Member

What changed

Replaces the terminal-window hero (identical structure across every AxeForge marketing site built this session) with something specific to what ReviewForge actually does.

New hero concept: mock PR review thread

  • A simplified Go diff (helpers/http.go, PR #42 "Add retry logic" — pulled directly from README.md's own learning-report example) plus three reviewer-persona comment bubbles with avatar badges: Bob Lil Swagger, Robert Dover Clow, Maya Simplifica — real persona names/voices from README.md's persona table, not invented. Each comment is written in that persona's documented style (Bob: friendly/encouraging, Robert: SOLID/pattern-naming, Maya: everyday analogies — a garden hose for defer).
  • Layout mirrors the family pattern instead of repeating it: PR thread on the left, hero copy on the right. Every sibling AxeForge site runs text-left/demo-right; this one flips it, with DOM order matching visual order at every breakpoint (so it's also the correct a11y reading order, not just a visual flip).

GSAP motion (CDN, no build step)

  • Hero entrance timeline: kicker → h1 → lede → ctas → PR thread shell → diff lines → then the three comments stream in one at a time (slide-in + a typing-style clip-path reveal on the paragraph + an avatar "pop"), not all at once — meant to read like a live review arriving.
  • Scroll-triggered reveals on every section below the fold (fade + translateY, staggered across cards), fired once per section via ScrollTrigger's once: true — verified it doesn't replay on scroll-direction changes.
  • Reduced-motion hard requirement: implemented via gsap.matchMedia() with two mutually-exclusive branches. When prefers-reduced-motion: reduce, every animated target is set to its final visible state immediately — verified with Playwright's reducedMotion: 'reduce' emulation (actual browser render, not just code review).
  • Fails open: nothing is hidden via CSS. Hiding only happens in JS right before its own reveal tween, guarded by typeof gsap === "undefined". If the CDN scripts are blocked/slow, the static page just renders fully visible — no permanently-stuck opacity: 0.

Bug caught and fixed along the way

The new PR-thread card sits on --af-surface-2/-3 — fixed dark tokens that don't change with page theme (intentional: "terminal stays dark on a light page"). I initially styled its content with var(--af-text)/var(--af-text-muted), which do flip to near-black in light theme — that would go invisible against the permanently-dark card. Fixed by hardcoding the shell's dark-mode text values for that component instead, and verified by forcing data-theme="light" in a real browser and screenshotting it.

Verification

  • Screenshotted at 1440×1600 and 375×1400 (Playwright/Chromium, not --virtual-time-budget headless — that mode doesn't tick requestAnimationFrame reliably and produced a false-looking "stuck mid-animation" screenshot on first pass; a real browser session with real waits confirmed the animation completes correctly).
  • Reduced-motion path verified via actual reducedMotion: 'reduce' browser emulation — confirmed all content renders in final state immediately, no animation.
  • Light theme verified by forcing data-theme="light" — confirmed the fix above holds.
  • Re-ran detect.mjs anti-pattern scan: identical 3 pre-existing advisory findings as the original file (grid background, single-font, em-dash density) — nothing new introduced.
  • Full 5-dimension audit re-run, all still 4/4 (20/20 total):
    • Accessibility 4/4 — heading hierarchy/landmarks unchanged, DOM order matches visual reading order in the mirrored hero, decorative avatar initials marked aria-hidden (redundant with adjacent persona name), diff add/remove meaning carried by literal +/- text (not color alone), reduced-motion hard requirement verified live.
    • Performance 4/4 — GSAP loaded at end of <body> (non-blocking), animations use transform/opacity, only 2 small CDN scripts added, no new images.
    • Theming 4/4 — the light-theme bug described above was caught and fixed before shipping, verified live in both themes.
    • Responsive 4/4 — hero grid columns use minmax(0, Xfr) (the known bug class from an earlier build), diff box has an overflow-x: auto safety net, PR file badge hides below 480px; mobile screenshot shows clean single-column stacking with no horizontal scroll.
    • Implementation Integrity 4/4 — old feed script/CSS fully removed (no dead code), new component reuses established tokens/patterns (feed-tone dots, glass-card conventions) rather than inventing new ones, content grounded in README's real persona table and learning-report example throughout.

Replace the terminal-window hero (shared boilerplate across every
AxeForge marketing site) with a hero specific to what ReviewForge
actually does: a mock PR review thread showing a simplified Go diff
plus three reviewer-persona comment bubbles (Bob Lil Swagger, Robert
Dover Clow, Maya Simplifica -- names/voices pulled straight from
README.md's persona table), grounded in the README's own "Add retry
logic" / helpers/http.go learning-report example.

Layout mirrors the family pattern instead of repeating it: the PR
thread sits on the left, hero copy on the right (siblings all run
text-left/demo-right), with DOM order matching visual order at every
breakpoint.

Adds GSAP (via CDN, no build step) for:
- hero entrance timeline: kicker -> h1 -> lede -> ctas -> PR thread
  shell -> diff lines -> the three comments streaming in one at a
  time (slide-in + typing-style clip-path reveal + avatar pop),
  not all at once
- scroll-triggered reveals per section (fade + translateY, staggered
  across cards), once per section via ScrollTrigger's `once: true`
- reduced-motion guard via gsap.matchMedia(): every animated target
  snaps to its final visible state immediately, no opacity:0 left
  behind; verified with Playwright's reducedMotion emulation
- fails open if the CDN scripts don't load: nothing is hidden via
  CSS, only via JS right before its reveal tween, so a blocked/slow
  CDN just leaves the static page fully visible

Also fixes a theme bug the new component would otherwise have
inherited: --af-surface-2/-3 are fixed dark tokens that don't change
with the page theme (by design, "terminal stays dark on a light
page"), but var(--af-text)/var(--af-text-muted) do -- pairing them
would go invisible in light theme. The PR card uses the shell's fixed
dark-mode text values instead, verified by forcing data-theme="light".
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

StructLint — 4 violation(s) found

58/62 rules passed · 4 violation(s) detected against .structlint.yaml.

Files not matching any allowed naming pattern (3)
  • File not in allowed naming pattern: web/axeforge.css
  • File not in allowed naming pattern: web/index.html
  • File not in allowed naming pattern: web/sitemap.xml
Directories not in the allowed list (1)
  • Directory not in allowed list: web

View full run · Powered by StructLint

@murilopmachado
murilopmachado merged commit 9303528 into main Aug 4, 2026
3 of 5 checks passed
@murilopmachado
murilopmachado deleted the polish/gsap-and-differentiation branch August 4, 2026 19:53
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.

1 participant