From 3cc022e2083c225f6de70536ff08e21d386824e3 Mon Sep 17 00:00:00 2001 From: Yeom-JinHo Date: Tue, 17 Mar 2026 21:37:39 +0900 Subject: [PATCH 1/4] fix(www): resolve nested anchor hydration error in testimonials --- apps/www/components/sections/testimonials.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/www/components/sections/testimonials.tsx b/apps/www/components/sections/testimonials.tsx index ec6227505..369a5a690 100644 --- a/apps/www/components/sections/testimonials.tsx +++ b/apps/www/components/sections/testimonials.tsx @@ -64,26 +64,25 @@ export function Testimonials() { {allTweets.map((id) => ( - +
-
+
- +
))} From 782d0cfd225e2fc762ffb16655fa5897ef504c0f Mon Sep 17 00:00:00 2001 From: Yeom-JinHo Date: Tue, 17 Mar 2026 21:51:49 +0900 Subject: [PATCH 2/4] fix(www): restore testimonial tweet card navigation without nested links --- .../sections/testimonial-tweet-link.tsx | 78 +++++++++++++++++++ apps/www/components/sections/testimonials.tsx | 25 ++---- 2 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 apps/www/components/sections/testimonial-tweet-link.tsx diff --git a/apps/www/components/sections/testimonial-tweet-link.tsx b/apps/www/components/sections/testimonial-tweet-link.tsx new file mode 100644 index 000000000..f79498864 --- /dev/null +++ b/apps/www/components/sections/testimonial-tweet-link.tsx @@ -0,0 +1,78 @@ +"use client" + +import type { KeyboardEvent, MouseEvent, ReactNode } from "react" +import Link from "next/link" +import { ArrowUpRight } from "lucide-react" + +import { Button } from "@/components/ui/button" + +interface TestimonialTweetLinkProps { + children: ReactNode + tweetUrl: string +} + +const interactiveSelector = + 'a, button, input, select, textarea, summary, [role="button"], [role="link"]' + +const isInteractiveTarget = (target: EventTarget | null) => { + if (!(target instanceof HTMLElement)) { + return false + } + + return target.closest(interactiveSelector) !== null +} + +export function TestimonialTweetLink({ + children, + tweetUrl, +}: TestimonialTweetLinkProps) { + const navigateToTweet = () => { + window.location.assign(tweetUrl) + } + + const handleClick = (event: MouseEvent) => { + if (isInteractiveTarget(event.target)) { + return + } + + navigateToTweet() + } + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.target !== event.currentTarget) { + return + } + + if (event.key !== "Enter" && event.key !== " ") { + return + } + + event.preventDefault() + navigateToTweet() + } + + return ( +
+ {children} +
+ +
+
+ ) +} diff --git a/apps/www/components/sections/testimonials.tsx b/apps/www/components/sections/testimonials.tsx index 369a5a690..d4823016f 100644 --- a/apps/www/components/sections/testimonials.tsx +++ b/apps/www/components/sections/testimonials.tsx @@ -1,8 +1,5 @@ -import Link from "next/link" -import { ArrowUpRight } from "lucide-react" - -import { Button } from "@/components/ui/button" import { ExpandableMasonarySection } from "@/components/sections/expandable-masonary-section" +import { TestimonialTweetLink } from "@/components/sections/testimonial-tweet-link" import { TweetCard } from "@/registry/magicui/tweet-card" const allTweets = [ @@ -64,25 +61,15 @@ export function Testimonials() { {allTweets.map((id) => ( -
+ -
- -
-
+ ))}
From b37c7cdf5f4560ce6438b4d6632c7325594d4f18 Mon Sep 17 00:00:00 2001 From: Yeom-JinHo Date: Tue, 17 Mar 2026 21:56:36 +0900 Subject: [PATCH 3/4] fix(www): restore testimonial card fallback navigation --- .../components/sections/testimonial-tweet-link.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/www/components/sections/testimonial-tweet-link.tsx b/apps/www/components/sections/testimonial-tweet-link.tsx index f79498864..0d26e574c 100644 --- a/apps/www/components/sections/testimonial-tweet-link.tsx +++ b/apps/www/components/sections/testimonial-tweet-link.tsx @@ -14,12 +14,17 @@ interface TestimonialTweetLinkProps { const interactiveSelector = 'a, button, input, select, textarea, summary, [role="button"], [role="link"]' -const isInteractiveTarget = (target: EventTarget | null) => { +const isInteractiveTarget = ( + target: EventTarget | null, + container: HTMLElement +) => { if (!(target instanceof HTMLElement)) { return false } - return target.closest(interactiveSelector) !== null + const interactiveElement = target.closest(interactiveSelector) + + return interactiveElement !== null && interactiveElement !== container } export function TestimonialTweetLink({ @@ -31,7 +36,7 @@ export function TestimonialTweetLink({ } const handleClick = (event: MouseEvent) => { - if (isInteractiveTarget(event.target)) { + if (isInteractiveTarget(event.target, event.currentTarget)) { return } From e2edb4f02d29bfe947bbc142b6492bffb4eca1b7 Mon Sep 17 00:00:00 2001 From: Yeom-JinHo Date: Tue, 17 Mar 2026 22:07:03 +0900 Subject: [PATCH 4/4] fix(www): handle svg click targets in testimonial card navigation guard --- apps/www/components/sections/testimonial-tweet-link.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/www/components/sections/testimonial-tweet-link.tsx b/apps/www/components/sections/testimonial-tweet-link.tsx index 0d26e574c..e6b430a46 100644 --- a/apps/www/components/sections/testimonial-tweet-link.tsx +++ b/apps/www/components/sections/testimonial-tweet-link.tsx @@ -16,9 +16,9 @@ const interactiveSelector = const isInteractiveTarget = ( target: EventTarget | null, - container: HTMLElement + container: Element ) => { - if (!(target instanceof HTMLElement)) { + if (!(target instanceof Element)) { return false }