Skip to content

feat: redesign testimonial marquee with manual controls, add security,headers, and fix SEO issues#335

Open
HEETMEHTA18 wants to merge 6 commits intokeploy:mainfrom
HEETMEHTA18:feat/testimonial-marquee-redesign
Open

feat: redesign testimonial marquee with manual controls, add security,headers, and fix SEO issues#335
HEETMEHTA18 wants to merge 6 commits intokeploy:mainfrom
HEETMEHTA18:feat/testimonial-marquee-redesign

Conversation

@HEETMEHTA18
Copy link
Copy Markdown

Related Tickets & Documents

Fixes: #3440

Description

This PR revamps the blog landing page’s testimonial section to provide a "flawless" user experience, addressing community feedback regarding UI aesthetics and security. It replaces the legacy marquee with a modern dual-row scrolling layout.

GDG CHARUSAT TEAM ID: <Team 143>

Changes

  • Implemented a modern dual-row infinite scroll marquee for community testimonials.
  • Enhanced card design with quote icons, hover effects, and skeleton loaders.
  • Implemented core security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) in next.config.js.
  • Fixed relative paths for favicons and RSS feeds in meta.tsx to ensure they load correctly under the /blog base path.
  • Standardized SEO tokens and fixed variable typos in pages/index.tsx.

Type of Change

  • Chore (maintenance, refactoring, tooling updates)
  • Bug fix (non-breaking change that fixes an issue)
  • New feature (change that adds functionality)
  • Breaking Change (may require updates in existing code)
  • UI improvement (visual or design changes)
  • Performance improvement (optimization or efficiency enhancements)
  • Documentation update (changes to README, guides, etc.)
  • CI (updates to continuous integration workflows)
  • Revert (undo a previous commit or merge)

Testing

  • Performed local verification using npm run dev.
  • Verified the marquee functionality and edge-blending aesthetics.
  • Checked responsiveness on mobile and tablet views.
  • Ran npm run build to ensure no production regressions or hydration errors.

Demo

  • Testimonial Section: Now features dual-row scrolling with edge-blending gradients for a premium feel.
  • Security: Headers verified in browser dev tools.
  • SEO: Favicons and metadata links now correctly resolve to the /blog subdirectory.
    -Before:
Engineering._.Keploy.Blog.-.Google.Chrome.2026-02-24.21-44-31.mp4

After:

localhost_3000_blog.-.Google.Chrome.2026-02-24.21-51-45.mp4
image

Environment and Dependencies

  • New Dependencies: None.
  • Configuration Changes: Updated next.config.js to include standard security headers.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added corresponding tests
  • I have run the build command to ensure there are no build errors
  • My changes have been tested across relevant browsers/devices
  • For UI changes, I've included visual evidence of my changes (Verified locally)

… headers, and fix SEO issues

Signed-off-by: Heet Mehta <heetmehta18125@gmail.com>
… issues

Signed-off-by: Heet Mehta <heetmehta18125@gmail.com>
@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented Feb 24, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

This PR includes several quality improvements:

  • Security: Added security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) in next.config.js
  • Bug Fix: Fixed missing leading slashes in favicon/manifest paths in meta.tsx
  • Accessibility: Added aria-hidden="true" to decorative SVG icons in footer.tsx
  • Robustness: Added fallback handling for missing title and coverImage in cover-image.tsx
  • UX: Improved testimonials component with hydration-safe mounting, better avatar fallback handling via image proxy, and enhanced styling
  • Code Quality: Fixed typo allTehcnologyPostsallTechnologyPosts and removed duplicate <Head> tag in pages/index.tsx
Files Reviewed (6 files)
  • components/cover-image.tsx - Fallback handling improvements
  • components/footer.tsx - Accessibility improvements
  • components/meta.tsx - Path fixes
  • components/testimonials.tsx - Major refactor with improved UX
  • next.config.js - Security headers added
  • pages/index.tsx - Typo fix and duplicate removal

Signed-off-by: Heet Mehta <heetmehta18125@gmail.com>
Signed-off-by: Heet Mehta <heetmehta18125@gmail.com>
@HEETMEHTA18
Copy link
Copy Markdown
Author

hey @amaan-bhati i have proposed a solution to the given issue of the testimonial marquee which is redesigned to the design which you have mentioned in the issue

@dhananjay6561
Copy link
Copy Markdown
Member

Hi @HEETMEHTA18.
Thank you for contributing and taking the initiative to work on this!
However, the issue mentioned in this PR has already been fixed in the repository. Because of that, we won’t be able to proceed with merging this one.

Feel free to look into other open issues in the repository and pick one that hasn’t been addressed yet. We’d be happy to review another contribution from you. Thanks again for your effort!

@HEETMEHTA18
Copy link
Copy Markdown
Author

@dhananjay6561 however but still in the website no furthur changes are made till i just made changes into this so that the avatar was not loading but in the given pr the changes are made.
image

Signed-off-by: Heet Mehta <heetmehta18125@gmail.com>
@dhananjay6561 dhananjay6561 requested a review from Copilot April 6, 2026 12:06
@dhananjay6561
Copy link
Copy Markdown
Member

Hey @HEETMEHTA18 👋 — thanks for putting this PR together, we appreciate the effort!

We've gone ahead and requested a Copilot review on this. Here's some context from the reviewer:

Too many unrelated changes — split into 2-3 PRs. Invalid transition-scale class. Title says 'manual controls' but none exist.

Once you've had a chance to go through the comments, please address the feedback and resolve the threads — and we'll get this across the line. Feel free to ask if anything's unclear. Happy coding! 💙

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the blog landing page experience by redesigning the testimonials marquee, tightening security headers at the Next.js edge, and fixing SEO-related asset paths under the /blog basePath.

Changes:

  • Replaced the legacy testimonials marquee with a dual-row, hover-pausing infinite scroll layout (with a skeleton fallback during initial mount).
  • Added standard security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) via next.config.js.
  • Fixed SEO/meta asset URLs to be basePath-correct and cleaned up SEO token/typo issues on the homepage.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pages/index.tsx Removes inline <Head> title usage (relying on Layout/Meta) and fixes a typo in technology posts variable naming.
next.config.js Adds additional security headers alongside the existing CSP.
components/testimonials.tsx Implements the redesigned dual-row marquee testimonial UI and avatar proxy/fallback handling.
components/meta.tsx Corrects favicon/manifest/RSS/browserconfig paths to work under /blog basePath.
components/footer.tsx Adds aria-hidden to decorative social SVG icons.
components/cover-image.tsx Adds safer fallbacks for alt/src and refactors className handling (but currently introduces a critical build issue).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +26 to +28
className={cn("rounded-md transition-border duration-300", imgClassName, {
" transition-scale duration-300": slug,
})}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CoverImage now uses cn(...) but the helper isn’t imported in this file, which will cause a runtime/compile failure. Also, the Tailwind classes transition-border and transition-scale don’t exist in this repo’s Tailwind config, so the intended transitions won’t apply; use valid Tailwind transition utilities (or extend Tailwind) instead.

Suggested change
className={cn("rounded-md transition-border duration-300", imgClassName, {
" transition-scale duration-300": slug,
})}
className={[
"rounded-md transition-colors duration-300",
slug ? "transition-transform duration-300" : "",
imgClassName ?? "",
]
.filter(Boolean)
.join(" ")}

Copilot uses AI. Check for mistakes.
Comment on lines +20 to 26
/** Fallback avatar component – renders a user icon/initials in a branded circle */
const FallbackAvatar = ({ name }: { name: string }) => (
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-500 ring-2 ring-gray-100 p-1">
<User className="h-full w-full opacity-60" />
</div>
);

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getInitials is declared but never used, and FallbackAvatar accepts a name prop but doesn’t render initials (only the icon). This is dead code / misleading intent—either remove the unused helper/prop or render initials as the fallback so the behavior matches the comment.

Suggested change
/** Fallback avatar component – renders a user icon/initials in a branded circle */
const FallbackAvatar = ({ name }: { name: string }) => (
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-500 ring-2 ring-gray-100 p-1">
<User className="h-full w-full opacity-60" />
</div>
);
/** Fallback avatar component – renders initials when available, otherwise a user icon */
const FallbackAvatar = ({ name }: { name: string }) => {
const initials = name.trim() ? getInitials(name) : "";
return (
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-500 ring-2 ring-gray-100 p-1">
{initials ? (
<span className="text-sm font-medium leading-none">{initials}</span>
) : (
<User className="h-full w-full opacity-60" />
)}
</div>
);
};

Copilot uses AI. Check for mistakes.
Comment thread pages/index.tsx
Comment on lines 26 to 36
return (

<Layout
preview={preview}
featuredImage={HOME_OG_IMAGE_URL}
Title={`Blog - Keploy`}
Description={"The Keploy Blog offers in-depth articles and expert insights on software testing, automation, and quality assurance, empowering developers to enhance their testing strategies and deliver robust applications."}
structuredData={structuredData}
>
<Head>
<title>{`Engineering | Keploy Blog`}</title>
</Head>
<Header />
<Container>
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After removing the inline <Head> usage, the Head import at the top of this file is now unused and will be flagged by next lint. Please remove the unused import to keep CI/lint clean.

Copilot uses AI. Check for mistakes.
Signed-off-by: Heet Mehta <heetmehta18125@gmail.com>
Copy link
Copy Markdown
Member

@amaan-bhati amaan-bhati left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Report — feat: redesign testimonial marquee with manual controls, add security headers, and fix SEO issues

Repo: keploy/blog-website | Author: HEETMEHTA18 | Review date: 2026-04-30
Change type: content-rendering-change, config-change, metadata-or-seo-change


Summary

Severity Count
🔴 Blocking 3
🟡 Warning 4
🔵 Suggestion 4
⚪ Nitpick 3

Reviewed files: components/cover-image.tsx, components/footer.tsx, components/meta.tsx, components/testimonials.tsx, next.config.js, pages/index.tsx


Gate checklist

  • No blocking findings remain
  • Metadata contract still holds on production pages (see title warning)
  • Base-path and route integrity remain valid
  • Impacted files were checked for regressions

🔴 Blocking

  • components/cover-image.tsxcn used but never imported
    Why: The PR replaces the inline className template literal with cn(...) but adds no import for cn. This is a build-breaking ReferenceError — the site will not compile.
    Fix:

    import { cn } from "@/lib/utils"; // verify path under the @/* alias
  • components/testimonials.tsx:~87 — Raw <img> tag instead of next/image
    Why: Framework rule #3 blocks new raw <img> tags in component code. The new ReviewCard uses <img> for avatars, bypassing Next.js image optimization (format selection, lazy loading queue, responsive srcsets). The pages/api/proxy-image.ts proxy already exists, so next/image with the proxy URL is the correct pattern.
    Fix:

    import NextImage from "next/image";
    // …
    <NextImage
      className="rounded-full object-cover ring-2 ring-primary-100"
      width={40}
      height={40}
      alt={name ? `${name}'s avatar` : "Avatar"}
      src={proxiedAvatar}
      onError={() => setImgError(true)}
    />
  • components/cover-image.tsx:~32priority hardcoded to true for all cover images
    Why: priority={priority} (caller-controlled, defaulting to false) was replaced with the bare priority literal (always true). Every cover image sitewide — including list-page thumbnails — will now be eagerly preloaded, regressing Lighthouse CWV scores (TBT, FCP, TTI).
    Fix: Restore the conditional props:

    priority={priority}
    loading={priority ? "eager" : "lazy"}
    sizes={sizes}

🟡 Warnings

  • components/cover-image.tsxsizes prop silently dropped
    Why: sizes is still declared in the Props interface and destructured, but the new <Image> render no longer passes it. This silently removes the responsive image hint callers relied on for bandwidth-optimal image selection.
    Fix: Restore sizes={sizes} to the <Image> element, or remove sizes from the interface if it is intentionally deprecated.

  • components/meta.tsx:40-41Group.svg does not exist in public/favicon/
    Why: The fork's public/favicon/ directory contains safari-pinned-tab.svg but not Group.svg (confirmed by directory listing). The <link rel="mask-icon"> will 404 in Safari on every page globally. (Group.png does exist — the shortcut-icon change is fine.)
    Fix: Revert the mask-icon line:

    <link rel="mask-icon" href="/blog/favicon/safari-pinned-tab.svg" color="#000000" />

    …or add Group.svg to public/favicon/ before merging.

  • pages/index.tsx:36-38 — Route-specific <title> removed; title coverage unverified
    Why: The <Head><title>{Engineering | Keploy Blog}</title></Head> block is removed. The Meta component (reviewed in full) does not emit a <title> tag — only OG, Twitter, description, and canonical meta. If Layout does not emit <title> from the Title prop, the homepage will have no title, which is a critical SEO regression.
    Fix: Either restore the route-specific <title> (framework rule #2 explicitly permits this for pages/index.tsx), or document that layout.tsx emits <title> from the Title prop and verify it in the browser before merging.

  • components/testimonials.tsxuseRouter called per ReviewCard instance
    Why: ReviewCard is rendered for every tweet (20+ cards). useRouter() inside each card creates per-instance router subscriptions. The only value consumed — router.basePath — is a static build-time constant that never changes at runtime.
    Fix: Resolve basePath once in TwitterTestimonials and pass it as a prop to ReviewCard.


🔵 Suggestions

  • components/testimonials.tsx:13-17getInitials() is defined with a JSDoc comment but never called. FallbackAvatar shows the User icon instead. Either use getInitials(name) for initials display inside FallbackAvatar, or delete the function.

  • components/testimonials.tsx:56-62 — The useEffect that resets imgError on avatar prop change is unnecessary. Prop changes already trigger a re-render; the effect adds an extra render cycle with no real benefit. Remove it, or use key={avatar} on ReviewCard at the call site for a cleaner reset.

  • components/testimonials.tsx:115-145 — The mounted guard renders a skeleton on SSR and replaces it with the marquee on the client, causing measurable CLS. Consider using opacity-0 / visibility: hidden on the SSR pass and fading in on mount to avoid the layout shift.

  • next.config.jsX-Frame-Options: DENY is present but the CSP is missing frame-ancestors 'none'. Modern browsers prefer the CSP directive; adding both provides defense-in-depth for legacy and modern browsers alike.


⚪ Nitpicks

  • components/testimonials.tsx:7import { User } from "lucide-react" is placed after import Tweets from "../services/Tweets", breaking conventional import grouping (external packages before internal services). Move it with the other third-party imports at the top.

  • components/cover-image.tsx:35 — Double leading space in " transition-scale duration-300".

  • components/cover-image.tsx:34-36transition-border and transition-scale are not valid Tailwind CSS utilities. Standard Tailwind includes transition-colors and transition-transform; the non-existent classes will produce no CSS output.


✅ Looks Good

  • Security headers (X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy) are well-chosen and do not conflict with the existing CSP.
  • pages/index.tsx typo fix: allTehcnologyPostsallTechnologyPosts is a clean correction with no regressions.
  • components/footer.tsx accessibility: aria-hidden="true" on all four decorative SVG icons is correct — they are purely visual.
  • Marquee key uniqueness: Appending -r1 / -r2 to tweet IDs correctly prevents duplicate React key warnings.
  • Proxy routing: External avatar URLs routed through pages/api/proxy-image.ts follows the established allowlist pattern for this repo.
  • meta.tsx base-path correctness: All favicon hrefs retain the /blog/ prefix, consistent with basePath config.
  • Duplicate twitter:image cleaned up: The pre-featuredImage-conditional duplicate meta tag is removed; the consolidated structure is cleaner.

Cross-file impact

Direct findings:

  • components/cover-image.tsx — broken cn import blocks the build; priority regression affects all post-list and post-detail pages
  • components/testimonials.tsx — raw <img> bypasses the Next.js image pipeline for all testimonial avatars
  • components/meta.tsxGroup.svg 404 affects the Safari pinned-tab icon on every page (global)
  • pages/index.tsx — removed <title> may leave the homepage without a title if Layout does not cover it

Inferred impact risks checked:

  • components/layout.tsx — not in diff; Meta confirmed not to emit <title>; title coverage by layout.tsx requires manual verification
  • pages/api/proxy-image.ts — confirmed present in base repo; proxy routing in testimonials is safe

Overall verdict

Do not merge in current state. Three blockers must be resolved first: the missing cn import (build failure), the raw <img> tag (framework rule violation), and the hardcoded priority={true} regression (performance). Confirm the homepage <title> is still emitted and fix the Group.svg 404 before this ships. The security header additions, footer accessibility improvements, and typo fix are all clean — once the blockers are addressed this PR will be in good shape.


Review generated by Keploy QA Agent · 2026-04-30

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.

4 participants