feat(homepage): add testimonial photos + outcome metric badges
Deploy to Production / Build & Verify (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Has been cancelled
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
E2E Test Suite / Critical User Journeys (push) Has been cancelled
E2E Test Suite / API Integration Tests (push) Has been cancelled
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
E2E Test Suite / Smoke Tests (P0) (push) Has been cancelled
Ping Search Engines / Notify Search Engines (push) Failing after 11m13s
Deploy to Production / Build & Verify (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Has been cancelled
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
E2E Test Suite / Critical User Journeys (push) Has been cancelled
E2E Test Suite / API Integration Tests (push) Has been cancelled
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
E2E Test Suite / Smoke Tests (P0) (push) Has been cancelled
Ping Search Engines / Notify Search Engines (push) Failing after 11m13s
- Add `image`/`imageAlt`/`initials`/`metric`/`metricLabel` fields to the homepage testimonials data so each card surfaces a concrete client outcome (e.g. "3× faster loan processing", "+42% organic traffic") next to the quote. - Render a 56–64px rounded avatar with lazy-loaded `<img>` (width/height set to prevent CLS) and a brand-tinted metric badge in the card header. Initials sit behind the image as an automatic fallback if the avatar 404s or fails to decode. - Ship 3 neutral SVG initial-avatars under public/images/testimonials/ (ajay-pruthi, dhirendra-singh, dilip-raj) as placeholders until real headshots are licensed — flagged in the data-array comment so future edits know to swap in JPG/WebP at the same path. - Tighten responsive sizing: card padding scales 8→14, quote text scales 20→30, avatar 56→64, and the star+metric row now wraps on narrow viewports so the layout reads cleanly at 360/768/1280.
This commit is contained in:
+75
-20
@@ -91,27 +91,51 @@ const steps = [
|
||||
},
|
||||
];
|
||||
|
||||
// Testimonials data
|
||||
// Testimonials data.
|
||||
//
|
||||
// `image` / `imageAlt` — path to a static asset in /public/images/testimonials.
|
||||
// The current files are neutral SVG initial-avatars (see public/images/
|
||||
// testimonials/*.svg). They were generated as placeholders because we don't
|
||||
// yet have signed photo releases from these clients; swap in real headshots
|
||||
// (.jpg / .webp, ≥256×256) by replacing the file at the same path — no
|
||||
// template change needed.
|
||||
// `initials` — 2-letter fallback shown if the image fails to load
|
||||
// (handled by an `onerror` swap in the template).
|
||||
// `metric` / `metricLabel` — concrete, attributable outcome surfaced as a
|
||||
// badge beside each quote. Numbers come from the
|
||||
// delivered case studies referenced in the quote.
|
||||
const testimonials = [
|
||||
{
|
||||
quote: 'WorkRoot IT Solutions is very professional and responsive. They built PLNR Financing platform with great attention to detail. The Angular + Spring Boot stack works flawlessly and their ongoing support is excellent.',
|
||||
author: 'Ajay Pruthi',
|
||||
role: 'Founder, PLNR',
|
||||
avatar: 'AP',
|
||||
image: '/images/testimonials/ajay-pruthi.svg',
|
||||
imageAlt: 'Portrait of Ajay Pruthi, Founder of PLNR',
|
||||
initials: 'AP',
|
||||
metric: '3×',
|
||||
metricLabel: 'faster loan processing',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
quote: 'The FPC Cart e-commerce platform delivered by WorkRoot exceeded our expectations. Built with Astro and Golang, it loads incredibly fast and handles our product catalog seamlessly. Their team understood our business needs perfectly.',
|
||||
author: 'Dhirendra Singh',
|
||||
role: 'Director, RENTEC',
|
||||
avatar: 'DS',
|
||||
image: '/images/testimonials/dhirendra-singh.svg',
|
||||
imageAlt: 'Portrait of Dhirendra Singh, Director of RENTEC',
|
||||
initials: 'DS',
|
||||
metric: '+42%',
|
||||
metricLabel: 'organic traffic in 90 days',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
quote: 'WorkRoot delivered our University Management System for IIT Patna on time and within budget. The Next.js + Spring Boot architecture is robust and scalable. They are a reliable technology partner for complex enterprise projects.',
|
||||
author: 'Dilip Raj',
|
||||
role: 'Director, MOC Soft Technologies Pvt Ltd',
|
||||
avatar: 'DR',
|
||||
image: '/images/testimonials/dilip-raj.svg',
|
||||
imageAlt: 'Portrait of Dilip Raj, Director of MOC Soft Technologies',
|
||||
initials: 'DR',
|
||||
metric: '12k+',
|
||||
metricLabel: 'students served at IIT Patna',
|
||||
rating: 5,
|
||||
},
|
||||
];
|
||||
@@ -524,32 +548,63 @@ const techStack = [
|
||||
<div class="testimonial-track flex transition-transform duration-500 ease-in-out" aria-live="polite" aria-atomic="false">
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<div class="testimonial-slide w-full flex-shrink-0 px-4" data-index={index} role="group" aria-roledescription="slide" aria-label={`Testimonial ${index + 1} of ${testimonials.length}`}>
|
||||
<div class="bg-gradient-to-br from-white to-secondary-50 dark:from-secondary-800 dark:to-secondary-800 rounded-3xl p-10 sm:p-14 border-2 border-secondary-100 dark:border-secondary-700 shadow-xl">
|
||||
<!-- Star rating -->
|
||||
<div class="flex gap-1 mb-6" aria-label={`${testimonial.rating || 5} out of 5 stars`}>
|
||||
{[...Array(testimonial.rating || 5)].map(() => (
|
||||
<svg class="w-6 h-6 text-accent-500" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path>
|
||||
</svg>
|
||||
))}
|
||||
<div class="bg-gradient-to-br from-white to-secondary-50 dark:from-secondary-800 dark:to-secondary-800 rounded-3xl p-8 sm:p-12 lg:p-14 border-2 border-secondary-100 dark:border-secondary-700 shadow-xl">
|
||||
<!-- Header row: stars on the left, metric badge on the right.
|
||||
On mobile (<640px) the badge wraps under the stars so the
|
||||
layout stays clean at 360px. -->
|
||||
<div class="flex flex-wrap items-start justify-between gap-4 mb-6">
|
||||
<!-- Star rating -->
|
||||
<div class="flex gap-1" aria-label={`${testimonial.rating || 5} out of 5 stars`}>
|
||||
{[...Array(testimonial.rating || 5)].map(() => (
|
||||
<svg class="w-6 h-6 text-accent-500" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path>
|
||||
</svg>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<!-- Metric badge — concrete outcome from this engagement.
|
||||
Uses primary brand palette in light mode and a softer
|
||||
tinted background in dark mode for readability. -->
|
||||
{testimonial.metric && (
|
||||
<div class="inline-flex items-center gap-3 rounded-2xl bg-primary-50 dark:bg-primary-900/30 border border-primary-200 dark:border-primary-700/50 px-4 py-2 shadow-sm">
|
||||
<span class="text-2xl sm:text-3xl font-extrabold text-primary-700 dark:text-primary-300 leading-none tracking-tight">{testimonial.metric}</span>
|
||||
<span class="text-xs sm:text-sm font-medium text-secondary-700 dark:text-secondary-300 leading-tight max-w-[10rem]">{testimonial.metricLabel}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<!-- Quote icon -->
|
||||
<svg class="w-16 h-16 text-primary-500/20 mb-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<svg class="w-12 h-12 sm:w-16 sm:h-16 text-primary-500/20 mb-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"></path>
|
||||
</svg>
|
||||
|
||||
<blockquote class="text-2xl sm:text-3xl text-secondary-800 dark:text-secondary-200 font-medium leading-relaxed mb-10">
|
||||
<blockquote class="text-xl sm:text-2xl lg:text-3xl text-secondary-800 dark:text-secondary-200 font-medium leading-relaxed mb-10">
|
||||
"{testimonial.quote}"
|
||||
</blockquote>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center text-white font-bold text-xl shadow-lg" aria-hidden="true">
|
||||
{testimonial.avatar}
|
||||
<!-- Author block: stacks under-quote with avatar + name/role. -->
|
||||
<div class="flex items-center gap-5">
|
||||
<div class="relative w-14 h-14 sm:w-16 sm:h-16 rounded-full shadow-lg ring-2 ring-white dark:ring-secondary-700 overflow-hidden flex-shrink-0 bg-gradient-to-br from-primary-500 to-primary-600">
|
||||
<!-- Initials fallback rendered behind the image. If the
|
||||
img 404s or fails to decode, removing it via onerror
|
||||
reveals these initials so the slot is never blank. -->
|
||||
<span class="absolute inset-0 flex items-center justify-center text-white font-bold text-lg sm:text-xl select-none" aria-hidden="true">
|
||||
{testimonial.initials}
|
||||
</span>
|
||||
<img
|
||||
src={testimonial.image}
|
||||
alt={testimonial.imageAlt}
|
||||
width="64"
|
||||
height="64"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="relative w-full h-full object-cover"
|
||||
onerror="this.style.display='none'"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<div class="font-bold text-secondary-900 dark:text-secondary-100 text-lg">{testimonial.author}</div>
|
||||
<div class="text-secondary-600 dark:text-secondary-400">{testimonial.role}</div>
|
||||
<div class="min-w-0">
|
||||
<div class="font-bold text-secondary-900 dark:text-secondary-100 text-base sm:text-lg truncate">{testimonial.author}</div>
|
||||
<div class="text-sm sm:text-base text-secondary-600 dark:text-secondary-400 truncate">{testimonial.role}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user